@c8y/ngx-components 1021.49.12 → 1021.51.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/aggregation/aggregation.model.d.ts.map +1 -0
- package/core/aggregation/aggregation.service.d.ts +44 -0
- package/core/aggregation/aggregation.service.d.ts.map +1 -0
- package/core/aggregation/index.d.ts +3 -0
- package/core/aggregation/index.d.ts.map +1 -0
- package/core/common/index.d.ts +0 -2
- package/core/common/index.d.ts.map +1 -1
- package/core/dashboard/wiget-time-context/aggregation-picker/aggregation-picker.component.d.ts +2 -2
- package/core/dashboard/wiget-time-context/aggregation-picker/aggregation-picker.component.d.ts.map +1 -1
- package/core/dashboard/wiget-time-context/widget-time-context-icon-bar/widget-time-context-icon-bar.component.d.ts +1 -1
- package/core/dashboard/wiget-time-context/widget-time-context-icon-bar/widget-time-context-icon-bar.component.d.ts.map +1 -1
- package/core/dashboard/wiget-time-context/widget-time-context.component.d.ts +1 -1
- package/core/dashboard/wiget-time-context/widget-time-context.component.d.ts.map +1 -1
- package/core/i18n/pattern-messages.data.d.ts +18 -0
- package/core/i18n/pattern-messages.data.d.ts.map +1 -1
- package/core/index.d.ts +1 -0
- package/core/index.d.ts.map +1 -1
- package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/data-fetching.service.d.ts +54 -42
- package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/data-fetching.service.d.ts.map +1 -1
- package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.component.d.ts +28 -8
- package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.component.d.ts.map +1 -1
- package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.service.d.ts +3 -52
- package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.service.d.ts.map +1 -1
- package/datapoints-export-selector/datapoints-export-selector.model.d.ts +5 -14
- package/datapoints-export-selector/datapoints-export-selector.model.d.ts.map +1 -1
- package/device-grid/device-grid.component.d.ts.map +1 -1
- package/device-grid/device-grid.service.d.ts +2 -0
- package/device-grid/device-grid.service.d.ts.map +1 -1
- package/esm2022/alarms/alarms-type-filter.component.mjs +3 -3
- package/esm2022/core/aggregation/aggregation.model.mjs +46 -0
- package/esm2022/core/aggregation/aggregation.service.mjs +76 -0
- package/esm2022/core/aggregation/index.mjs +3 -0
- package/esm2022/core/common/index.mjs +1 -3
- package/esm2022/core/dashboard/wiget-time-context/aggregation-picker/aggregation-picker.component.mjs +2 -2
- package/esm2022/core/dashboard/wiget-time-context/widget-time-context-icon-bar/widget-time-context-icon-bar.component.mjs +2 -2
- package/esm2022/core/dashboard/wiget-time-context/widget-time-context.component.mjs +4 -4
- package/esm2022/core/i18n/pattern-messages.data.mjs +19 -1
- package/esm2022/core/index.mjs +2 -1
- package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/data-fetching.service.mjs +166 -164
- package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.component.mjs +82 -48
- package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.service.mjs +5 -95
- package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-exports-selector-data-scope/datapoints-exports-selector-data-scope.component.mjs +3 -3
- package/esm2022/datapoints-export-selector/datapoints-export-selector.component.mjs +3 -3
- package/esm2022/datapoints-export-selector/datapoints-export-selector.model.mjs +1 -9
- package/esm2022/device-grid/device-grid.component.mjs +3 -3
- package/esm2022/device-grid/device-grid.service.mjs +16 -1
- package/esm2022/device-list/add-smart-group.component.mjs +3 -3
- package/esm2022/interval-picker/interval-picker.model.mjs +5 -1
- package/esm2022/operations/bulk-operation-list-item/bulk-operation-list-item.service.mjs +5 -4
- package/esm2022/operations/operation-details/operation-details.module.mjs +7 -7
- package/esm2022/operations/operations-list/device-control.feature.mjs +20 -0
- package/esm2022/operations/operations-list/index.mjs +4 -3
- package/esm2022/operations/operations-list/operations-list-item.component.mjs +11 -9
- package/esm2022/operations/operations-list/operations-list.component.mjs +15 -10
- package/esm2022/operations/operations-list/operations-list.module.mjs +16 -41
- package/esm2022/operations/operations-list/operations-list.service.mjs +4 -3
- package/esm2022/operations/operations.module.mjs +21 -14
- package/esm2022/operations/shared/operations.service.mjs +4 -3
- package/esm2022/services/index.mjs +14 -3
- package/esm2022/services/service-command-tab/c8y-ngx-components-services-service-command-tab.mjs +5 -0
- package/esm2022/services/service-command-tab/index.mjs +3 -0
- package/esm2022/services/service-command-tab/service-command-tab.component.mjs +33 -0
- package/esm2022/services/service-command-tab/service-command.feature.mjs +14 -0
- package/esm2022/services/services-device-tab/index.mjs +9 -3
- package/esm2022/services/services-device-tab/services-device-tab.component.mjs +3 -4
- package/esm2022/services/services-device-tab/services-device-tab.guard.mjs +2 -2
- package/esm2022/services/services-device-tab/services.breadcrumb-factory.mjs +13 -1
- package/esm2022/services/services.module.mjs +4 -2
- package/esm2022/services/shared/c8y-ngx-components-services-shared.mjs +5 -0
- package/esm2022/services/shared/index.mjs +3 -0
- package/esm2022/services/shared/service-command.service.mjs +118 -0
- package/esm2022/services/shared/services.model.mjs +23 -0
- package/esm2022/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.component.mjs +25 -41
- package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-reload/datapoints-reload.component.mjs +3 -3
- package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.component.mjs +55 -49
- package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.service.mjs +25 -73
- package/esm2022/widgets/implementations/datapoints-table/datapoints-table-widget.model.mjs +20 -20
- package/esm2022/widgets/implementations/datapoints-table/datapoints-table.service.mjs +19 -19
- package/fesm2022/c8y-ngx-components-alarms.mjs +2 -2
- package/fesm2022/c8y-ngx-components-alarms.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-datapoints-export-selector.mjs +248 -308
- package/fesm2022/c8y-ngx-components-datapoints-export-selector.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-device-grid.mjs +17 -2
- package/fesm2022/c8y-ngx-components-device-grid.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-device-list.mjs +2 -2
- package/fesm2022/c8y-ngx-components-device-list.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-interval-picker.mjs +5 -1
- package/fesm2022/c8y-ngx-components-interval-picker.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-operations-bulk-operation-list-item.mjs +3 -2
- package/fesm2022/c8y-ngx-components-operations-bulk-operation-list-item.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-operations-operation-details.mjs +5 -5
- package/fesm2022/c8y-ngx-components-operations-operation-details.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-operations-operations-list.mjs +57 -52
- package/fesm2022/c8y-ngx-components-operations-operations-list.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-operations-shared.mjs +3 -2
- package/fesm2022/c8y-ngx-components-operations-shared.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-operations.mjs +42 -35
- package/fesm2022/c8y-ngx-components-operations.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-services-service-command-tab.mjs +49 -0
- package/fesm2022/c8y-ngx-components-services-service-command-tab.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-services-shared.mjs +145 -0
- package/fesm2022/c8y-ngx-components-services-shared.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-services.mjs +20 -113
- package/fesm2022/c8y-ngx-components-services.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs +1075 -1253
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components.mjs +136 -76
- package/fesm2022/c8y-ngx-components.mjs.map +1 -1
- package/interval-picker/interval-picker.model.d.ts +6 -0
- package/interval-picker/interval-picker.model.d.ts.map +1 -1
- package/locales/de.po +52 -41
- package/locales/es.po +52 -41
- package/locales/fr.po +51 -40
- package/locales/ja_JP.po +53 -43
- package/locales/ko.po +51 -34
- package/locales/locales.pot +42 -31
- package/locales/nl.po +52 -41
- package/locales/pl.po +52 -40
- package/locales/pt_BR.po +51 -40
- package/locales/zh_CN.po +50 -33
- package/locales/zh_TW.po +50 -33
- package/operations/bulk-operation-list-item/bulk-operation-list-item.service.d.ts +2 -2
- package/operations/bulk-operation-list-item/bulk-operation-list-item.service.d.ts.map +1 -1
- package/operations/operation-details/operation-details.module.d.ts +1 -1
- package/operations/operations-list/device-control.feature.d.ts +4 -0
- package/operations/operations-list/device-control.feature.d.ts.map +1 -0
- package/operations/operations-list/index.d.ts +3 -2
- package/operations/operations-list/index.d.ts.map +1 -1
- package/operations/operations-list/operations-list-item.component.d.ts +1 -1
- package/operations/operations-list/operations-list-item.component.d.ts.map +1 -1
- package/operations/operations-list/operations-list.component.d.ts +3 -1
- package/operations/operations-list/operations-list.component.d.ts.map +1 -1
- package/operations/operations-list/operations-list.module.d.ts +3 -9
- package/operations/operations-list/operations-list.module.d.ts.map +1 -1
- package/operations/operations.module.d.ts +1 -2
- package/operations/operations.module.d.ts.map +1 -1
- package/package.json +1 -1
- package/services/index.d.ts +13 -2
- package/services/index.d.ts.map +1 -1
- package/services/service-command-tab/c8y-ngx-components-services-service-command-tab.d.ts.map +1 -0
- package/services/service-command-tab/index.d.ts +3 -0
- package/services/service-command-tab/index.d.ts.map +1 -0
- package/services/service-command-tab/service-command-tab.component.d.ts +12 -0
- package/services/service-command-tab/service-command-tab.component.d.ts.map +1 -0
- package/services/service-command-tab/service-command.feature.d.ts +3 -0
- package/services/service-command-tab/service-command.feature.d.ts.map +1 -0
- package/services/services-device-tab/index.d.ts +8 -2
- package/services/services-device-tab/index.d.ts.map +1 -1
- package/services/services-device-tab/services-device-tab.component.d.ts +2 -2
- package/services/services-device-tab/services-device-tab.component.d.ts.map +1 -1
- package/services/services-device-tab/services.breadcrumb-factory.d.ts.map +1 -1
- package/services/services.module.d.ts.map +1 -1
- package/services/shared/c8y-ngx-components-services-shared.d.ts.map +1 -0
- package/services/shared/index.d.ts +3 -0
- package/services/shared/index.d.ts.map +1 -0
- package/services/{services-device-tab → shared}/service-command.service.d.ts +10 -7
- package/services/shared/service-command.service.d.ts.map +1 -0
- package/services/shared/services.model.d.ts +31 -0
- package/services/shared/services.model.d.ts.map +1 -0
- package/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.component.d.ts +3 -5
- package/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.component.d.ts.map +1 -1
- package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.component.d.ts +7 -2
- package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.component.d.ts.map +1 -1
- package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.service.d.ts +7 -27
- package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.service.d.ts.map +1 -1
- package/widgets/implementations/datapoints-table/datapoints-table-widget.model.d.ts +4 -3
- package/widgets/implementations/datapoints-table/datapoints-table-widget.model.d.ts.map +1 -1
- package/widgets/implementations/datapoints-table/datapoints-table.service.d.ts +5 -5
- package/widgets/implementations/datapoints-table/datapoints-table.service.d.ts.map +1 -1
- package/core/common/aggregation/aggregation.model.d.ts.map +0 -1
- package/core/common/aggregation/aggregation.service.d.ts +0 -17
- package/core/common/aggregation/aggregation.service.d.ts.map +0 -1
- package/esm2022/core/common/aggregation/aggregation.model.mjs +0 -46
- package/esm2022/core/common/aggregation/aggregation.service.mjs +0 -34
- package/esm2022/services/services-device-tab/service-command.service.mjs +0 -113
- package/esm2022/services/services-device-tab/service-device-tab.model.mjs +0 -2
- package/esm2022/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.service.mjs +0 -124
- package/services/services-device-tab/service-command.service.d.ts.map +0 -1
- package/services/services-device-tab/service-device-tab.model.d.ts +0 -11
- package/services/services-device-tab/service-device-tab.model.d.ts.map +0 -1
- package/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.service.d.ts +0 -56
- package/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.service.d.ts.map +0 -1
- /package/core/{common/aggregation → aggregation}/aggregation.model.d.ts +0 -0
|
@@ -3,24 +3,24 @@ import { Injectable, EventEmitter, inject, Component, ChangeDetectionStrategy, I
|
|
|
3
3
|
import * as i3 from '@angular/forms';
|
|
4
4
|
import { ControlContainer, ReactiveFormsModule, FormControl, Validators } from '@angular/forms';
|
|
5
5
|
import * as i1$1 from '@c8y/ngx-components';
|
|
6
|
-
import { gettext, CoreModule,
|
|
6
|
+
import { gettext, CoreModule, AGGREGATION_LABELS, AGGREGATION_VALUES_ARR, AGGREGATION_VALUES, IntervalBasedReload, CountdownIntervalComponent, CommonModule as CommonModule$1, CountdownIntervalModule, ListGroupModule, DynamicComponentAlertAggregator, DynamicComponentAlert, DocsModule, DynamicComponentModule, DismissAlertStrategy } from '@c8y/ngx-components';
|
|
7
7
|
import * as i4 from '@c8y/ngx-components/context-dashboard';
|
|
8
|
-
import * as
|
|
8
|
+
import * as i6 from '@c8y/ngx-components/datapoint-selector';
|
|
9
9
|
import { DatapointSelectorModule } from '@c8y/ngx-components/datapoint-selector';
|
|
10
|
-
import
|
|
11
|
-
import
|
|
10
|
+
import * as i1 from '@c8y/ngx-components/datapoints-export-selector';
|
|
11
|
+
import { dateRangeValidator, DatapointsExportSelectorComponent } from '@c8y/ngx-components/datapoints-export-selector';
|
|
12
|
+
import { INTERVAL_VALUES } from '@c8y/ngx-components/interval-picker';
|
|
13
|
+
import * as i7 from 'ngx-bootstrap/popover';
|
|
12
14
|
import { PopoverModule } from 'ngx-bootstrap/popover';
|
|
13
15
|
import { Subject, takeUntil, merge, debounceTime } from 'rxjs';
|
|
14
|
-
import * as i1 from '@c8y/client';
|
|
15
|
-
import { aggregationType } from '@c8y/client';
|
|
16
16
|
import * as i2 from '@angular/common';
|
|
17
17
|
import { CommonModule } from '@angular/common';
|
|
18
|
-
import {
|
|
18
|
+
import { aggregationType } from '@c8y/client';
|
|
19
19
|
import * as i1$2 from '@ngx-translate/core';
|
|
20
20
|
import * as i4$1 from 'ngx-bootstrap/tooltip';
|
|
21
21
|
import { TooltipModule } from 'ngx-bootstrap/tooltip';
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const DEFAULT_DPT_REFRESH_INTERVAL_VALUE = 30_000;
|
|
24
24
|
const DATE_SELECTION_VALUES = {
|
|
25
25
|
// Will be uncommented in MTM-61920
|
|
26
26
|
// dashboard_context: 'dashboard_context',
|
|
@@ -45,12 +45,12 @@ const RENDER_TYPES_LABELS = {
|
|
|
45
45
|
area: gettext('Area')
|
|
46
46
|
};
|
|
47
47
|
const INTERVAL_VALUES_ARR = [
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
INTERVAL_VALUES.minutes,
|
|
49
|
+
INTERVAL_VALUES.hours,
|
|
50
|
+
INTERVAL_VALUES.days,
|
|
51
|
+
INTERVAL_VALUES.weeks,
|
|
52
|
+
INTERVAL_VALUES.months,
|
|
53
|
+
INTERVAL_VALUES.custom
|
|
54
54
|
];
|
|
55
55
|
const TIME_RANGE_INTERVAL_LABELS = {
|
|
56
56
|
minutes: gettext('Last minute'),
|
|
@@ -62,1379 +62,1197 @@ const TIME_RANGE_INTERVAL_LABELS = {
|
|
|
62
62
|
};
|
|
63
63
|
const DURATION_OPTIONS = [
|
|
64
64
|
{
|
|
65
|
-
id:
|
|
65
|
+
id: INTERVAL_VALUES.minutes,
|
|
66
66
|
label: TIME_RANGE_INTERVAL_LABELS.minutes,
|
|
67
|
-
unit:
|
|
67
|
+
unit: INTERVAL_VALUES.minutes,
|
|
68
68
|
amount: 1
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
|
-
id:
|
|
71
|
+
id: INTERVAL_VALUES.hours,
|
|
72
72
|
label: TIME_RANGE_INTERVAL_LABELS.hours,
|
|
73
|
-
unit:
|
|
73
|
+
unit: INTERVAL_VALUES.hours,
|
|
74
74
|
amount: 1
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
|
-
id:
|
|
77
|
+
id: INTERVAL_VALUES.days,
|
|
78
78
|
label: TIME_RANGE_INTERVAL_LABELS.days,
|
|
79
|
-
unit:
|
|
79
|
+
unit: INTERVAL_VALUES.days,
|
|
80
80
|
amount: 1
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
|
-
id:
|
|
83
|
+
id: INTERVAL_VALUES.weeks,
|
|
84
84
|
label: TIME_RANGE_INTERVAL_LABELS.weeks,
|
|
85
|
-
unit:
|
|
85
|
+
unit: INTERVAL_VALUES.weeks,
|
|
86
86
|
amount: 1
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
|
-
id:
|
|
89
|
+
id: INTERVAL_VALUES.months,
|
|
90
90
|
label: TIME_RANGE_INTERVAL_LABELS.months,
|
|
91
|
-
unit:
|
|
91
|
+
unit: INTERVAL_VALUES.months,
|
|
92
92
|
amount: 1
|
|
93
93
|
},
|
|
94
|
-
{ id:
|
|
94
|
+
{ id: INTERVAL_VALUES.custom, label: TIME_RANGE_INTERVAL_LABELS.custom }
|
|
95
95
|
];
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
class DatapointsTableViewService {
|
|
101
|
-
constructor(measurementService) {
|
|
102
|
-
this.measurementService = measurementService;
|
|
97
|
+
class DatapointsTableService {
|
|
98
|
+
constructor(dataFetchingService) {
|
|
99
|
+
this.dataFetchingService = dataFetchingService;
|
|
103
100
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
101
|
+
calculateDateRange(interval) {
|
|
102
|
+
const now = new Date();
|
|
103
|
+
const nowString = now.toISOString();
|
|
104
|
+
let dateFrom;
|
|
105
|
+
switch (interval) {
|
|
106
|
+
case INTERVAL_VALUES.minutes:
|
|
107
|
+
dateFrom = this.dataFetchingService.adjustDate(nowString, -1, true);
|
|
108
|
+
break;
|
|
109
|
+
case INTERVAL_VALUES.hours:
|
|
110
|
+
const minutesInAnHourNegative = -60;
|
|
111
|
+
dateFrom = this.dataFetchingService.adjustDate(nowString, minutesInAnHourNegative, true);
|
|
112
|
+
break;
|
|
113
|
+
case INTERVAL_VALUES.days:
|
|
114
|
+
const minutesInADayNegative = -24 * 60;
|
|
115
|
+
dateFrom = this.dataFetchingService.adjustDate(nowString, minutesInADayNegative, true);
|
|
116
|
+
break;
|
|
117
|
+
case INTERVAL_VALUES.weeks:
|
|
118
|
+
const minutesInAWeekNegative = -7 * 24 * 60;
|
|
119
|
+
dateFrom = this.dataFetchingService.adjustDate(nowString, minutesInAWeekNegative, true);
|
|
120
|
+
break;
|
|
121
|
+
case INTERVAL_VALUES.months:
|
|
122
|
+
const oneMonthAgo = new Date(now);
|
|
123
|
+
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
|
|
124
|
+
dateFrom = this.dataFetchingService.adjustDate(oneMonthAgo.toISOString(), 0, true);
|
|
125
|
+
break;
|
|
126
|
+
default:
|
|
127
|
+
throw new Error('Invalid time interval');
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
dateFrom: dateFrom,
|
|
131
|
+
dateTo: this.dataFetchingService.adjustDate(nowString, 0, true)
|
|
132
|
+
};
|
|
112
133
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
134
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableService, deps: [{ token: i1.DataFetchingService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
135
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableService, providedIn: 'root' }); }
|
|
136
|
+
}
|
|
137
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableService, decorators: [{
|
|
138
|
+
type: Injectable,
|
|
139
|
+
args: [{
|
|
140
|
+
providedIn: 'root'
|
|
141
|
+
}]
|
|
142
|
+
}], ctorParameters: () => [{ type: i1.DataFetchingService }] });
|
|
143
|
+
|
|
144
|
+
class DateRangePickerComponent {
|
|
145
|
+
constructor() {
|
|
146
|
+
/**
|
|
147
|
+
* If set to true, the component will be reactive and will emit the updatedDate on every change.
|
|
148
|
+
* Otherwise, the component will use a non emitting variant of a template.
|
|
149
|
+
*/
|
|
150
|
+
this.isEmittingDateChange = false;
|
|
151
|
+
/**
|
|
152
|
+
* Determines the display of from and to date picker labels.
|
|
153
|
+
*/
|
|
154
|
+
this.showLabel = true;
|
|
155
|
+
this.updatedDate = new EventEmitter();
|
|
156
|
+
this.DATE_FROM = 'dateFrom';
|
|
157
|
+
this.DATE_TO = 'dateTo';
|
|
158
|
+
this.FROM_DATE = gettext('From`date`');
|
|
159
|
+
this.HAS_ERROR = 'has-error';
|
|
160
|
+
this.INVALID_DATE_TIME = 'invalidDateTime';
|
|
161
|
+
this.THIS_DATE_IS_INVALID = gettext('This date is invalid.');
|
|
162
|
+
this.THIS_DATE_IS_AFTER_THE_LAST_ALLOWED_DATE = gettext('This date is after the latest allowed date.');
|
|
163
|
+
this.THIS_DATE_IS_BEFORE_THE_EARLIEST_ALLOWED_DATE = gettext('This date is before the earliest allowed date.');
|
|
164
|
+
this.TO_DATE = gettext('To`date`');
|
|
165
|
+
this.parentContainer = inject(ControlContainer);
|
|
116
166
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
* ```typescript
|
|
122
|
-
* new Map([
|
|
123
|
-
* [
|
|
124
|
-
* "844657202",
|
|
125
|
-
* [
|
|
126
|
-
* "c8y_Temperature.T"
|
|
127
|
-
* ]
|
|
128
|
-
* ],
|
|
129
|
-
* [
|
|
130
|
-
* "32666427",
|
|
131
|
-
* [
|
|
132
|
-
* "c8y_Battery.Battery"
|
|
133
|
-
* ]
|
|
134
|
-
* ]
|
|
135
|
-
* ]);
|
|
136
|
-
* ```
|
|
137
|
-
* @param datapoints - An array of data points.
|
|
138
|
-
* @returns A map where the key is the data point ID and the value is an array of data point series.
|
|
139
|
-
*/
|
|
140
|
-
groupSeriesByDeviceId(activeDatapoints) {
|
|
141
|
-
return activeDatapoints.reduce((map, { fragment, series, __target: { id } }) => {
|
|
142
|
-
const value = `${fragment}.${series}`;
|
|
143
|
-
const existingValue = map.get(id) ?? [];
|
|
144
|
-
map.set(id, [...existingValue, value]);
|
|
145
|
-
return map;
|
|
146
|
-
}, new Map());
|
|
167
|
+
onDateFromChange(dateFrom) {
|
|
168
|
+
this.updatedDate.emit({
|
|
169
|
+
dateFrom
|
|
170
|
+
});
|
|
147
171
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
* @param datapointsValuesDataMap - A map of data point sources with their associated series.
|
|
152
|
-
* @param config - The configuration of the data points table.
|
|
153
|
-
* @param roundSeconds - Whether to round the seconds or not.
|
|
154
|
-
* If true, the seconds will be rounded to 0.
|
|
155
|
-
* If false, the seconds will be displayed as they are.
|
|
156
|
-
* @returns A Promise that resolves to a Map object with data point IDs as keys and DataObject as values or undefined when all series has forbidden access.
|
|
157
|
-
*/
|
|
158
|
-
async getAllActiveSeriesDataMap(datapointsValuesDataMap, config, roundSeconds) {
|
|
159
|
-
const promises = Array.from(datapointsValuesDataMap).map(async ([source, series]) => {
|
|
160
|
-
const params = {
|
|
161
|
-
dateFrom: config.dateFrom,
|
|
162
|
-
dateTo: config.dateTo,
|
|
163
|
-
source,
|
|
164
|
-
series,
|
|
165
|
-
aggregationType: config.aggregation
|
|
166
|
-
};
|
|
167
|
-
// TODO: consider adding a cache mechanism -> MTM-59233
|
|
168
|
-
const { data, res } = await this.loadSeriesData(params, roundSeconds);
|
|
169
|
-
return { source, data, res };
|
|
172
|
+
onDateToChange(dateTo) {
|
|
173
|
+
this.updatedDate.emit({
|
|
174
|
+
dateTo
|
|
170
175
|
});
|
|
171
|
-
const results = await Promise.all(promises);
|
|
172
|
-
const allSeriesHasForbiddenAccess = results.every(item => item.res?.status === 403);
|
|
173
|
-
if (allSeriesHasForbiddenAccess) {
|
|
174
|
-
throw new Error('Access forbidden: All series have a 403 status code');
|
|
175
|
-
}
|
|
176
|
-
const filteredResults = this.filterOutElementsWithForbiddenResponses(results);
|
|
177
|
-
const resultMap = new Map();
|
|
178
|
-
filteredResults.forEach(result => resultMap.set(result.source, result.data));
|
|
179
|
-
return resultMap;
|
|
180
176
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
177
|
+
get parentFormGroup() {
|
|
178
|
+
return this.parentContainer.control;
|
|
179
|
+
}
|
|
180
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DateRangePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
181
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DateRangePickerComponent, isStandalone: true, selector: "c8y-date-range-picker", inputs: { isEmittingDateChange: "isEmittingDateChange", showLabel: "showLabel" }, outputs: { updatedDate: "updatedDate" }, ngImport: i0, template: "<div class=\"d-flex gap-8 a-i-center flex-wrap\">\n <c8y-form-group\n [ngClass]=\"[\n parentFormGroup?.controls.dateFrom.errors ? HAS_ERROR : '',\n isEmittingDateChange ? 'd-flex a-i-center gap-4 m-b-0' : ''\n ]\"\n >\n <ng-container *ngIf=\"showLabel\">\n <label\n [title]=\"FROM_DATE | translate\"\n [for]=\"DATE_FROM\"\n >\n {{ FROM_DATE | translate }}\n </label>\n </ng-container>\n <c8y-date-time-picker\n id=\"DATE_FROM\"\n [maxDate]=\"parentFormGroup?.value.dateTo\"\n [placeholder]=\"FROM_DATE | translate\"\n [formControl]=\"parentFormGroup?.controls.dateFrom\"\n [ngClass]=\"parentFormGroup?.controls.dateFrom.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"onDateFromChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages\n class=\"text-nowrap\"\n [show]=\"parentFormGroup?.controls.dateFrom.errors\"\n >\n <c8y-message\n name=\"dateAfterRangeMax\"\n [text]=\"THIS_DATE_IS_AFTER_THE_LAST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n <c8y-form-group\n [ngClass]=\"[\n parentFormGroup?.controls.dateTo.errors ? HAS_ERROR : '',\n isEmittingDateChange ? 'd-flex a-i-center gap-4 m-b-0' : ''\n ]\"\n >\n <ng-container *ngIf=\"showLabel\">\n <label\n [title]=\"TO_DATE | translate\"\n [for]=\"DATE_TO\"\n >\n {{ TO_DATE | translate }}\n </label>\n </ng-container>\n <c8y-date-time-picker\n id=\"DATE_TO\"\n [minDate]=\"parentFormGroup?.value.dateFrom\"\n [placeholder]=\"TO_DATE | translate\"\n [formControl]=\"parentFormGroup?.controls.dateTo\"\n [ngClass]=\"parentFormGroup?.controls.dateTo.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"onDateToChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"parentFormGroup?.controls.dateTo.errors\">\n <c8y-message\n name=\"dateBeforeRangeMin\"\n [text]=\"THIS_DATE_IS_BEFORE_THE_EARLIEST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "pipe", type: i1$1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "component", type: i1$1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1$1.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i1$1.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: i1$1.DateTimePickerComponent, selector: "c8y-date-time-picker", inputs: ["minDate", "maxDate", "placeholder", "dateInputFormat", "adaptivePosition", "size", "dateType", "config"], outputs: ["onDateSelected"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }], viewProviders: [
|
|
182
|
+
{
|
|
183
|
+
provide: ControlContainer,
|
|
184
|
+
useFactory: () => inject(ControlContainer, { skipSelf: true })
|
|
185
|
+
}
|
|
186
|
+
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
187
|
+
}
|
|
188
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DateRangePickerComponent, decorators: [{
|
|
189
|
+
type: Component,
|
|
190
|
+
args: [{ selector: 'c8y-date-range-picker', standalone: true, imports: [CoreModule, CommonModule, ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.OnPush, viewProviders: [
|
|
191
|
+
{
|
|
192
|
+
provide: ControlContainer,
|
|
193
|
+
useFactory: () => inject(ControlContainer, { skipSelf: true })
|
|
194
|
+
}
|
|
195
|
+
], template: "<div class=\"d-flex gap-8 a-i-center flex-wrap\">\n <c8y-form-group\n [ngClass]=\"[\n parentFormGroup?.controls.dateFrom.errors ? HAS_ERROR : '',\n isEmittingDateChange ? 'd-flex a-i-center gap-4 m-b-0' : ''\n ]\"\n >\n <ng-container *ngIf=\"showLabel\">\n <label\n [title]=\"FROM_DATE | translate\"\n [for]=\"DATE_FROM\"\n >\n {{ FROM_DATE | translate }}\n </label>\n </ng-container>\n <c8y-date-time-picker\n id=\"DATE_FROM\"\n [maxDate]=\"parentFormGroup?.value.dateTo\"\n [placeholder]=\"FROM_DATE | translate\"\n [formControl]=\"parentFormGroup?.controls.dateFrom\"\n [ngClass]=\"parentFormGroup?.controls.dateFrom.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"onDateFromChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages\n class=\"text-nowrap\"\n [show]=\"parentFormGroup?.controls.dateFrom.errors\"\n >\n <c8y-message\n name=\"dateAfterRangeMax\"\n [text]=\"THIS_DATE_IS_AFTER_THE_LAST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n <c8y-form-group\n [ngClass]=\"[\n parentFormGroup?.controls.dateTo.errors ? HAS_ERROR : '',\n isEmittingDateChange ? 'd-flex a-i-center gap-4 m-b-0' : ''\n ]\"\n >\n <ng-container *ngIf=\"showLabel\">\n <label\n [title]=\"TO_DATE | translate\"\n [for]=\"DATE_TO\"\n >\n {{ TO_DATE | translate }}\n </label>\n </ng-container>\n <c8y-date-time-picker\n id=\"DATE_TO\"\n [minDate]=\"parentFormGroup?.value.dateFrom\"\n [placeholder]=\"TO_DATE | translate\"\n [formControl]=\"parentFormGroup?.controls.dateTo\"\n [ngClass]=\"parentFormGroup?.controls.dateTo.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"onDateToChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"parentFormGroup?.controls.dateTo.errors\">\n <c8y-message\n name=\"dateBeforeRangeMin\"\n [text]=\"THIS_DATE_IS_BEFORE_THE_EARLIEST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n</div>\n" }]
|
|
196
|
+
}], propDecorators: { isEmittingDateChange: [{
|
|
197
|
+
type: Input
|
|
198
|
+
}], showLabel: [{
|
|
199
|
+
type: Input
|
|
200
|
+
}], updatedDate: [{
|
|
201
|
+
type: Output
|
|
202
|
+
}] } });
|
|
203
|
+
|
|
204
|
+
function minOneDatapointActive() {
|
|
205
|
+
return (control) => {
|
|
206
|
+
const datapoints = control.value;
|
|
207
|
+
if (!datapoints || !datapoints.length) {
|
|
208
|
+
return null;
|
|
204
209
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const hasMoreThanOneDot = series.some(s => s.split('.').length > 2);
|
|
209
|
-
if (!hasMoreThanOneDot) {
|
|
210
|
-
filter.series = series;
|
|
210
|
+
const activeDatapoints = datapoints.filter(datapoint => datapoint.__active);
|
|
211
|
+
if (activeDatapoints.length >= 1) {
|
|
212
|
+
return null;
|
|
211
213
|
}
|
|
212
|
-
return
|
|
214
|
+
return { exactlyOneDatapointNeedsToBeActive: true };
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
class DatapointsTableWidgetConfigComponent {
|
|
218
|
+
constructor(aggregationService, datapointsTableService, formBuilder, form, widgetConfig) {
|
|
219
|
+
this.aggregationService = aggregationService;
|
|
220
|
+
this.datapointsTableService = datapointsTableService;
|
|
221
|
+
this.formBuilder = formBuilder;
|
|
222
|
+
this.form = form;
|
|
223
|
+
this.widgetConfig = widgetConfig;
|
|
224
|
+
this.AGGREGATION_LABELS = AGGREGATION_LABELS;
|
|
225
|
+
this.DATE_SELECTION_LABELS = DATE_SELECTION_LABELS;
|
|
226
|
+
// Will be uncommented in MTM-61920
|
|
227
|
+
// readonly DEFAULT_DATE_SELECTOR_VALUE = DATE_SELECTION_VALUES.dashboard_context;
|
|
228
|
+
this.DEFAULT_DATE_SELECTOR_VALUE = DATE_SELECTION_VALUES.config;
|
|
229
|
+
this.DEFAULT_INTERVAL_VALUE = INTERVAL_VALUES.hours;
|
|
230
|
+
this.TIME_RANGE_INTERVAL_LABELS = TIME_RANGE_INTERVAL_LABELS;
|
|
231
|
+
this.AGGREGATION_VALUES_ARR = AGGREGATION_VALUES_ARR;
|
|
232
|
+
this.DATE_SELECTION_VALUES_ARR = DATE_SELECTION_VALUES_ARR;
|
|
233
|
+
this.INTERVAL_VALUES_ARR = INTERVAL_VALUES_ARR;
|
|
234
|
+
this.REFRESH_INTERVAL_VALUES_ARR = REFRESH_INTERVAL_VALUES_ARR;
|
|
235
|
+
this.datapointSelectionConfig = {};
|
|
236
|
+
this.disabledAggregationOptions = {};
|
|
237
|
+
this.defaultFormOptions = {
|
|
238
|
+
selectableChartLineTypes: [],
|
|
239
|
+
selectableAxisTypes: [],
|
|
240
|
+
showRedRange: true,
|
|
241
|
+
showYellowRange: true
|
|
242
|
+
};
|
|
243
|
+
this.decimalLimits = {
|
|
244
|
+
numberOfDecimalPlacesMin: 0,
|
|
245
|
+
numberOfDecimalPlacesMax: 10
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* Indicate when the time interval selector item has been changed programmatically.
|
|
249
|
+
*
|
|
250
|
+
* This property is used to track changes in the time interval selector
|
|
251
|
+
* that are not triggered by direct user interaction, but by the application itself.
|
|
252
|
+
*
|
|
253
|
+
* In our case, the date selector and the interval selector are linked.
|
|
254
|
+
* So, when one of them changes, it affects the other.
|
|
255
|
+
* For example, selecting "Last hour" in the interval selector should automatically update the date range to the last hour.
|
|
256
|
+
* But without this flag, changing the date range would also update the interval selector, resulting in "Custom date" being selected.
|
|
257
|
+
* This happens because the system would interpret the behavior this way.
|
|
258
|
+
*/
|
|
259
|
+
this.isIntervalSelectorChangedProgrammatically = false;
|
|
260
|
+
this.destroy$ = new Subject();
|
|
213
261
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
* Adjusts the given date by adding the specified number of minutes and setting seconds to 0.
|
|
219
|
-
*
|
|
220
|
-
* @param date - The date to be adjusted in string format.
|
|
221
|
-
* @param minutes - The number of minutes to add to the date.
|
|
222
|
-
* @param roundSeconds - Whether to round the seconds or not.
|
|
223
|
-
* If true, the seconds will be rounded to 0.
|
|
224
|
-
* If false, the seconds will be displayed as they are.
|
|
225
|
-
* @returns The adjusted date in ISO string format.
|
|
226
|
-
*/
|
|
227
|
-
adjustDate(date, minutes, roundSeconds = true) {
|
|
228
|
-
const dateTime = new Date(date);
|
|
229
|
-
if (isNaN(dateTime.getTime())) {
|
|
230
|
-
throw new Error('Invalid date');
|
|
262
|
+
ngOnInit() {
|
|
263
|
+
// Condition will be removed in MTM-61920
|
|
264
|
+
if (this.config.widgetInstanceGlobalTimeContext) {
|
|
265
|
+
this.config.widgetInstanceGlobalTimeContext = false;
|
|
231
266
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
dateTime.setUTCSeconds(0, 0);
|
|
267
|
+
if (this.widgetConfig.context?.id) {
|
|
268
|
+
this.datapointSelectionConfig.contextAsset = this.widgetConfig?.context;
|
|
235
269
|
}
|
|
236
|
-
|
|
270
|
+
this.isWidgetLinkedToGlobalTimeContext = this.config.widgetInstanceGlobalTimeContext;
|
|
271
|
+
this.initForm();
|
|
272
|
+
this.handleAutoRefreshToggleChanges();
|
|
273
|
+
this.handleIntervalSelectorChanges();
|
|
274
|
+
this.handleDateSelectorChanges();
|
|
275
|
+
this.handleGlobalDateSelectorChanges();
|
|
276
|
+
this.updateDisabledAggregationOptions();
|
|
237
277
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
* Finds an index of a current data point within series object and based on that index filters values array.
|
|
242
|
-
*
|
|
243
|
-
* @param datapoints - An array of data points.
|
|
244
|
-
* @param datapointsSeriesDataMap - A map containing series data for data points.
|
|
245
|
-
* @returns An array of DatapointsWithValues.
|
|
246
|
-
*/
|
|
247
|
-
getDatapointsWithValues(datapoints, datapointsSeriesDataMap) {
|
|
248
|
-
return datapoints.map((dp) => {
|
|
249
|
-
const seriesData = datapointsSeriesDataMap.get(dp.__target.id);
|
|
250
|
-
if (!seriesData) {
|
|
251
|
-
return { ...dp, values: {} };
|
|
252
|
-
}
|
|
253
|
-
// Find an index of a corresponding datapoint data, within series object.
|
|
254
|
-
const datapointSeriesArrayIndex = seriesData.series.findIndex(s => s.name === dp.series && s.type === dp.fragment);
|
|
255
|
-
const valuesFilteredByDatapointSeriesArrayIndex = Object.fromEntries(Object.entries(seriesData.values).map(([key, arr]) => [
|
|
256
|
-
key,
|
|
257
|
-
arr.filter((_, index) => index === datapointSeriesArrayIndex)
|
|
258
|
-
]));
|
|
259
|
-
return {
|
|
260
|
-
...dp,
|
|
261
|
-
seriesUnit: seriesData.series[datapointSeriesArrayIndex]?.unit,
|
|
262
|
-
values: valuesFilteredByDatapointSeriesArrayIndex
|
|
263
|
-
};
|
|
264
|
-
});
|
|
278
|
+
ngOnDestroy() {
|
|
279
|
+
this.destroy$.next();
|
|
280
|
+
this.destroy$.complete();
|
|
265
281
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
});
|
|
282
|
+
onBeforeSave(config) {
|
|
283
|
+
if (this.formGroup.value.aggregation === AGGREGATION_VALUES.none ||
|
|
284
|
+
(!this.formGroup.value.aggregation && config.aggregation === AGGREGATION_VALUES.none)) {
|
|
285
|
+
// The 'NONE' aggregation type is not handled by a backend, so it simply needs to be deleted to get data without aggregation.
|
|
286
|
+
delete this.formGroup.value.aggregation;
|
|
287
|
+
delete config.aggregation;
|
|
288
|
+
}
|
|
289
|
+
this.updateTimeContext(config, this.formGroup.value.globalDateSelector);
|
|
290
|
+
Object.assign(config, this.formGroup.value);
|
|
276
291
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
return Object.entries(dp.values).flatMap(([date, valuesArray]) => {
|
|
283
|
-
const value = this.findMinMaxValues(valuesArray);
|
|
284
|
-
if (value == null) {
|
|
285
|
-
return [];
|
|
286
|
-
}
|
|
287
|
-
return [
|
|
288
|
-
{
|
|
289
|
-
dateAndTime: date,
|
|
290
|
-
deviceName: dp.__target.name,
|
|
291
|
-
fragment: dp.fragment,
|
|
292
|
-
label: dp.label,
|
|
293
|
-
redRangeMax: dp.redRangeMax,
|
|
294
|
-
redRangeMin: dp.redRangeMin,
|
|
295
|
-
renderType: dp.renderType,
|
|
296
|
-
series: dp.series,
|
|
297
|
-
value: value,
|
|
298
|
-
yellowRangeMax: dp.yellowRangeMax,
|
|
299
|
-
yellowRangeMin: dp.yellowRangeMin
|
|
300
|
-
}
|
|
301
|
-
];
|
|
302
|
-
});
|
|
303
|
-
});
|
|
292
|
+
toggleRefreshIntervalControl() {
|
|
293
|
+
this.formGroup.controls.isAutoRefreshEnabled.value
|
|
294
|
+
? this.formGroup.controls.refreshInterval.disable()
|
|
295
|
+
: this.formGroup.controls.refreshInterval.enable();
|
|
304
296
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
*/
|
|
321
|
-
findMinMaxValues(valuesArray) {
|
|
322
|
-
const validItems = valuesArray.filter((item) => item && item.min != null && item.max != null);
|
|
323
|
-
if (validItems.length === 0) {
|
|
324
|
-
return null;
|
|
297
|
+
updateTimeContext(config, selectedDateContext) {
|
|
298
|
+
switch (selectedDateContext) {
|
|
299
|
+
// Condition will be removed in MTM-61920
|
|
300
|
+
// case DATE_SELECTION_VALUES.dashboard_context:
|
|
301
|
+
// config.displayDateSelection = false;
|
|
302
|
+
// config.widgetInstanceGlobalTimeContext = true;
|
|
303
|
+
// break;
|
|
304
|
+
case DATE_SELECTION_VALUES.view_and_config:
|
|
305
|
+
config.displayDateSelection = true;
|
|
306
|
+
config.widgetInstanceGlobalTimeContext = false;
|
|
307
|
+
break;
|
|
308
|
+
case DATE_SELECTION_VALUES.config:
|
|
309
|
+
default:
|
|
310
|
+
config.displayDateSelection = false;
|
|
311
|
+
config.widgetInstanceGlobalTimeContext = false;
|
|
325
312
|
}
|
|
326
|
-
const initialValue = { min: validItems[0].min, max: validItems[0].max };
|
|
327
|
-
return validItems.reduce((acc, item) => ({
|
|
328
|
-
min: Math.min(acc.min, item.min),
|
|
329
|
-
max: Math.max(acc.max, item.max)
|
|
330
|
-
}), initialValue);
|
|
331
313
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
314
|
+
initForm() {
|
|
315
|
+
this.formGroup = this.createForm();
|
|
316
|
+
// When the 'NONE' aggregation is removed at the 'onBeforeSave' block,
|
|
317
|
+
// and a Global Date Context forces widgets auto refresh to be enabled
|
|
318
|
+
// then, the aggregation selector will be empty on initial load.
|
|
319
|
+
this.config.aggregation = this.config.aggregation ?? AGGREGATION_VALUES.none;
|
|
320
|
+
this.form.form.addControl('config', this.formGroup);
|
|
321
|
+
this.formGroup.patchValue(this.config);
|
|
322
|
+
}
|
|
323
|
+
createForm() {
|
|
324
|
+
const { dateFrom, dateTo } = this.datapointsTableService.calculateDateRange(this.DEFAULT_INTERVAL_VALUE);
|
|
325
|
+
const isLegacyWidget = this.config.realtime !== undefined && !this.config.decimalPlaces;
|
|
326
|
+
return this.formBuilder.group({
|
|
327
|
+
aggregation: new FormControl({
|
|
328
|
+
value: AGGREGATION_VALUES.none,
|
|
329
|
+
disabled: !this.isAutoRefershDisabled()
|
|
330
|
+
}),
|
|
331
|
+
datapoints: this.formBuilder.control(new Array(), [
|
|
332
|
+
Validators.required,
|
|
333
|
+
Validators.minLength(1),
|
|
334
|
+
minOneDatapointActive()
|
|
335
|
+
]),
|
|
336
|
+
globalDateSelector: new FormControl(this.DEFAULT_DATE_SELECTOR_VALUE),
|
|
337
|
+
dateFrom: new FormControl(dateFrom),
|
|
338
|
+
dateTo: new FormControl(dateTo),
|
|
339
|
+
decimalPlaces: [
|
|
340
|
+
2,
|
|
341
|
+
[
|
|
342
|
+
Validators.required,
|
|
343
|
+
Validators.min(this.decimalLimits.numberOfDecimalPlacesMin),
|
|
344
|
+
Validators.max(this.decimalLimits.numberOfDecimalPlacesMax),
|
|
345
|
+
Validators.pattern('^[0-9]+$')
|
|
346
|
+
]
|
|
347
|
+
],
|
|
348
|
+
interval: new FormControl(this.DEFAULT_INTERVAL_VALUE),
|
|
349
|
+
isAutoRefreshEnabled: isLegacyWidget ? this.config.realtime : [true],
|
|
350
|
+
refreshInterval: new FormControl({
|
|
351
|
+
value: DEFAULT_DPT_REFRESH_INTERVAL_VALUE,
|
|
352
|
+
disabled: this.isAutoRefershDisabled()
|
|
353
|
+
})
|
|
354
|
+
}, { validators: dateRangeValidator });
|
|
355
|
+
}
|
|
356
|
+
isAutoRefershDisabled() {
|
|
357
|
+
const isInitialWidgetCreation = Object.keys(this.config).length === 1 && this.config['settings'];
|
|
358
|
+
if (isInitialWidgetCreation) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
const isLegacyWidgetRealtimeNotActive = !this.config.realtime &&
|
|
362
|
+
!this.config.decimalPlaces &&
|
|
363
|
+
!this.config.refreshInterval &&
|
|
364
|
+
!this.config.isAutoRefreshEnabled;
|
|
365
|
+
if (isLegacyWidgetRealtimeNotActive) {
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
return !this.config.isAutoRefreshEnabled;
|
|
342
369
|
}
|
|
343
370
|
/**
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
* This function processes the provided data points and organizes them into a map structure
|
|
347
|
-
* where each key is a unique combination of date and device identifiers, and the value is an
|
|
348
|
-
* array of data points (or null values) associated with that key. This structured data is then
|
|
349
|
-
* used in the data point table to render the data appropriately.
|
|
371
|
+
* Handles changes to the auto-refresh toggle control and updates the aggregation control accordingly.
|
|
350
372
|
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
373
|
+
* This method subscribes to the value changes of the auto-refresh toggle form control.
|
|
374
|
+
* When auto-refresh is enabled, the aggregation control's value is set to 'none' and the control is
|
|
375
|
+
* visually disabled (but not programmatically disabled to ensure the value is saved).
|
|
376
|
+
* When auto-refresh is disabled, the aggregation control is enabled.
|
|
354
377
|
*/
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (!map.has(datapointIdentifier)) {
|
|
364
|
-
map.set(datapointIdentifier, Array(columnsHeadersReferences.length).fill(null));
|
|
378
|
+
handleAutoRefreshToggleChanges() {
|
|
379
|
+
this.formGroup.controls.isAutoRefreshEnabled.valueChanges
|
|
380
|
+
.pipe(takeUntil(this.destroy$))
|
|
381
|
+
.subscribe((isAutoRefreshEnabled) => {
|
|
382
|
+
const aggregationControl = this.formGroup.controls.aggregation;
|
|
383
|
+
if (isAutoRefreshEnabled) {
|
|
384
|
+
aggregationControl.setValue(AGGREGATION_VALUES.none);
|
|
385
|
+
// Do not disable the control programmatically to ensure the value is saved.
|
|
365
386
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
// Update the map entry with the data point.
|
|
369
|
-
const tableItem = map.get(datapointIdentifier);
|
|
370
|
-
if (tableItem) {
|
|
371
|
-
tableItem[matchingColumnIndex] = { ...obj };
|
|
387
|
+
else {
|
|
388
|
+
aggregationControl.enable();
|
|
372
389
|
}
|
|
373
390
|
});
|
|
374
|
-
return map;
|
|
375
391
|
}
|
|
376
392
|
/**
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
* @param map - The map containing the data points to be merged.
|
|
380
|
-
* @returns An array of grouped data point table items.
|
|
393
|
+
* Handles changes in the interval selector form control.
|
|
381
394
|
*/
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
dateAndTime: dateKey,
|
|
390
|
-
deviceName: deviceName,
|
|
391
|
-
rowItems: values.map(item => (item ? { ...item } : null))
|
|
392
|
-
});
|
|
395
|
+
handleIntervalSelectorChanges() {
|
|
396
|
+
this.formGroup.controls.interval.valueChanges
|
|
397
|
+
.pipe(takeUntil(this.destroy$))
|
|
398
|
+
.subscribe((selectedInterval) => {
|
|
399
|
+
if (selectedInterval === INTERVAL_VALUES.custom) {
|
|
400
|
+
this.updateDisabledAggregationOptions();
|
|
401
|
+
return;
|
|
393
402
|
}
|
|
403
|
+
this.handleNonCustomInterval(selectedInterval);
|
|
394
404
|
});
|
|
395
|
-
return mergedData;
|
|
396
405
|
}
|
|
397
|
-
|
|
398
|
-
|
|
406
|
+
handleNonCustomInterval(selectedInterval) {
|
|
407
|
+
this.isIntervalSelectorChangedProgrammatically = true;
|
|
408
|
+
this.updateDateRange(selectedInterval);
|
|
409
|
+
this.updateDisabledAggregationOptions();
|
|
410
|
+
this.updateAggregationIfAutoRefreshDisabled(selectedInterval);
|
|
399
411
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
};
|
|
412
|
+
updateDateRange(selectedInterval) {
|
|
413
|
+
const dateRange = this.datapointsTableService.calculateDateRange(selectedInterval);
|
|
414
|
+
this.formGroup.controls.dateTo.setValue(dateRange.dateTo);
|
|
415
|
+
// MTM-61351
|
|
416
|
+
// Use requestAnimationFrame to queue the dateFrom update.
|
|
417
|
+
// This prevents timing issues where rapid setValue calls might
|
|
418
|
+
// cause the view to go out of sync with form control values,
|
|
419
|
+
// especially during the first change after initialization.
|
|
420
|
+
// requestAnimationFrame(() => {
|
|
421
|
+
/**
|
|
422
|
+
* Without this requestAnimationFrame or setTimeout, dateFrom won't change to a correct value after first selector change.
|
|
423
|
+
* When form is saved it saves with a correct value, even without with this fix.
|
|
424
|
+
* How to reproduce:
|
|
425
|
+
* 1. set date values to e.g.: 01.05.2024-30.05.2024 and save it
|
|
426
|
+
* 2. reopen a config and switch interval to last month
|
|
427
|
+
*/
|
|
428
|
+
// This fix brakes a logic behind disabling non available aggregations.
|
|
429
|
+
// Form will be still saved with correct date value, only view is out of a sync.
|
|
430
|
+
this.formGroup.controls.dateFrom.setValue(dateRange.dateFrom);
|
|
431
|
+
this.isIntervalSelectorChangedProgrammatically = false;
|
|
432
|
+
// });
|
|
433
|
+
}
|
|
434
|
+
updateDisabledAggregationOptions() {
|
|
435
|
+
this.disabledAggregationOptions = this.aggregationService.getDisabledAggregationOptions(this.formGroup.controls.dateFrom.value, this.formGroup.controls.dateTo.value);
|
|
436
|
+
}
|
|
437
|
+
updateAggregationIfAutoRefreshDisabled(selectedInterval) {
|
|
438
|
+
const isAutoRefreshDisabled = !this.formGroup.controls.isAutoRefreshEnabled.value;
|
|
439
|
+
if (isAutoRefreshDisabled) {
|
|
440
|
+
this.setAggregationValue(selectedInterval);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
setAggregationValue(interval) {
|
|
444
|
+
const aggregationControl = this.formGroup.controls.aggregation;
|
|
445
|
+
const newAggregationValue = this.aggregationService.determineAggregationValue(interval);
|
|
446
|
+
aggregationControl.setValue(newAggregationValue);
|
|
421
447
|
}
|
|
422
448
|
/**
|
|
423
|
-
*
|
|
424
|
-
*
|
|
425
|
-
* @param date - The original date.
|
|
426
|
-
* @param amount - The amount of time units to subtract.
|
|
427
|
-
* @param unit - The unit of time to subtract (e.g., minutes, hours, days, weeks, months).
|
|
428
|
-
* @returns A new date with the specified time subtracted.
|
|
449
|
+
* Handles changes in the date selector form control.
|
|
429
450
|
*/
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
newDate.setUTCMinutes(newDate.getUTCMinutes() - amount);
|
|
435
|
-
break;
|
|
436
|
-
case TIME_RANGE_INTERVAL_UNITS_VALUES.hours:
|
|
437
|
-
newDate.setUTCHours(newDate.getUTCHours() - amount);
|
|
438
|
-
break;
|
|
439
|
-
case TIME_RANGE_INTERVAL_UNITS_VALUES.days:
|
|
440
|
-
newDate.setUTCDate(newDate.getUTCDate() - amount);
|
|
441
|
-
break;
|
|
442
|
-
case TIME_RANGE_INTERVAL_UNITS_VALUES.weeks:
|
|
443
|
-
newDate.setUTCDate(newDate.getUTCDate() - amount * 7);
|
|
444
|
-
break;
|
|
445
|
-
case TIME_RANGE_INTERVAL_UNITS_VALUES.months:
|
|
446
|
-
this.subtractMonthsAndAdjustDay(newDate, amount);
|
|
447
|
-
break;
|
|
448
|
-
}
|
|
449
|
-
return newDate;
|
|
451
|
+
handleDateSelectorChanges() {
|
|
452
|
+
merge(this.formGroup.controls.dateFrom.valueChanges, this.formGroup.controls.dateTo.valueChanges)
|
|
453
|
+
.pipe(takeUntil(this.destroy$))
|
|
454
|
+
.subscribe(() => this.handleDateChange());
|
|
450
455
|
}
|
|
451
|
-
|
|
452
|
-
if (
|
|
453
|
-
|
|
454
|
-
// It means that the user does not have permission to see any of the selected datapoints data.
|
|
455
|
-
return Array.from(activeDatapointsIdsWithSeries, mapToSourceValueObject);
|
|
456
|
+
handleDateChange() {
|
|
457
|
+
if (this.isIntervalSelectorChangedProgrammatically) {
|
|
458
|
+
return;
|
|
456
459
|
}
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
.
|
|
460
|
-
|
|
460
|
+
const intervalControl = this.formGroup.controls.interval;
|
|
461
|
+
if (intervalControl.value === INTERVAL_VALUES.custom) {
|
|
462
|
+
this.handleCustomIntervalDateChange();
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
this.setIntervalToCustom(intervalControl);
|
|
466
|
+
}
|
|
467
|
+
this.setToFirstAvailableAggregationOptionIfCurrentAggregationIsDisabled();
|
|
468
|
+
}
|
|
469
|
+
handleCustomIntervalDateChange() {
|
|
470
|
+
this.updateDisabledAggregationOptions();
|
|
471
|
+
}
|
|
472
|
+
setIntervalToCustom(intervalControl) {
|
|
473
|
+
intervalControl.setValue(INTERVAL_VALUES.custom);
|
|
461
474
|
}
|
|
462
475
|
/**
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
* @param dateStr - The date string to convert.
|
|
466
|
-
* @returns The ISO format of the given date string.
|
|
476
|
+
* Handles changes in the global date selector form control.
|
|
467
477
|
*/
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
478
|
+
handleGlobalDateSelectorChanges() {
|
|
479
|
+
this.formGroup.controls.globalDateSelector.valueChanges
|
|
480
|
+
.pipe(takeUntil(this.destroy$))
|
|
481
|
+
.subscribe(() => {
|
|
482
|
+
// .subscribe((selected: string) => {
|
|
483
|
+
// Condition will be removed in MTM-61920
|
|
484
|
+
// if (selected === DATE_SELECTION_VALUES.dashboard_context) {
|
|
485
|
+
// this.handleDashboardContext();
|
|
486
|
+
// } else {
|
|
487
|
+
this.handleNonDashboardContext();
|
|
488
|
+
// }
|
|
474
489
|
});
|
|
475
490
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
491
|
+
handleDashboardContext() {
|
|
492
|
+
this.isWidgetLinkedToGlobalTimeContext = true;
|
|
493
|
+
}
|
|
494
|
+
handleNonDashboardContext() {
|
|
495
|
+
this.isWidgetLinkedToGlobalTimeContext = false;
|
|
496
|
+
if (this.isConfigSavedWithoutDashboardTimeOption()) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
this.updateAggregationIfNecessary();
|
|
500
|
+
this.setIntervalToCustom(this.formGroup.controls.interval);
|
|
501
|
+
}
|
|
502
|
+
isConfigSavedWithoutDashboardTimeOption() {
|
|
503
|
+
return !this.config.widgetInstanceGlobalTimeContext;
|
|
504
|
+
}
|
|
505
|
+
updateAggregationIfNecessary() {
|
|
506
|
+
const aggregationControl = this.formGroup.controls.aggregation;
|
|
507
|
+
const isDashboardTimeOptionSetAggregationToNull = !aggregationControl.value;
|
|
508
|
+
if (isDashboardTimeOptionSetAggregationToNull) {
|
|
509
|
+
aggregationControl.setValue(AGGREGATION_VALUES.none);
|
|
484
510
|
}
|
|
485
511
|
}
|
|
486
512
|
/**
|
|
487
|
-
*
|
|
488
|
-
* Handles negative month numbers by normalizing them to the valid 0-11 range.
|
|
513
|
+
* Sets the aggregation control to the first available (non-disabled) option if the current option is disabled.
|
|
489
514
|
*
|
|
490
|
-
*
|
|
491
|
-
* -
|
|
492
|
-
* -
|
|
493
|
-
* -
|
|
515
|
+
* This method:
|
|
516
|
+
* - Retrieves the current value of the aggregation control.
|
|
517
|
+
* - Checks if the current aggregation option is disabled.
|
|
518
|
+
* - If the current option is disabled, sets the control to the first available (non-disabled) option based on the following order:
|
|
519
|
+
* - If the current value is `DAILY`, it switches to `HOURLY` if it's not disabled, otherwise to `MINUTELY` if `HOURLY` is also disabled.
|
|
520
|
+
* - If the current value is `HOURLY`, it switches to `MINUTELY` if it's not disabled.
|
|
521
|
+
* - If all options are disabled, it sets the value to `NONE`.
|
|
494
522
|
*
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
|
|
499
|
-
calculateTargetMonth(currentMonth, monthsToSubtract) {
|
|
500
|
-
return (currentMonth - monthsToSubtract + 12) % 12;
|
|
501
|
-
}
|
|
502
|
-
/**
|
|
503
|
-
* Sets the date to the last day of the previous month.
|
|
504
|
-
* Using 0 as dateValue makes JavaScript automatically calculate
|
|
505
|
-
* last day of previous month, per JavaScript Date API behavior.
|
|
506
|
-
* @param date - Date to modify
|
|
523
|
+
* The disabled state is stored in the `disabledAggregationOptions` object,
|
|
524
|
+
* where the key is the aggregation value and the value is a boolean indicating whether the option is disabled.
|
|
525
|
+
*
|
|
526
|
+
* The `AGGREGATION_VALUES` object defines the possible aggregation values.
|
|
507
527
|
*/
|
|
508
|
-
|
|
509
|
-
|
|
528
|
+
setToFirstAvailableAggregationOptionIfCurrentAggregationIsDisabled() {
|
|
529
|
+
const aggregationControl = this.formGroup.controls.aggregation;
|
|
530
|
+
const currentValue = aggregationControl.value;
|
|
531
|
+
const newAggregationValue = this.aggregationService.determineFirstNewAvailableAggregationValue(currentValue, this.disabledAggregationOptions);
|
|
532
|
+
if (newAggregationValue !== currentValue) {
|
|
533
|
+
aggregationControl.setValue(newAggregationValue);
|
|
534
|
+
}
|
|
510
535
|
}
|
|
511
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type:
|
|
512
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableViewService, providedIn: 'root' }); }
|
|
536
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableWidgetConfigComponent, deps: [{ token: i1$1.AggregationService }, { token: DatapointsTableService }, { token: i3.FormBuilder }, { token: i3.NgForm }, { token: i4.WidgetConfigComponent }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
537
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DatapointsTableWidgetConfigComponent, isStandalone: true, selector: "c8y-datapoints-table-view-config", inputs: { config: "config" }, ngImport: i0, template: "<div class=\"p-l-24 p-r-24\">\n <form\n class=\"no-card-context\"\n [formGroup]=\"formGroup\"\n >\n <!-- <datapoints-selector> -->\n <div class=\"col-md-6\">\n <div class=\"row\">\n <!-- global-time-context-selector -->\n <div class=\"form-group m-b-0 p-b-16 separator-bottom\">\n <label\n class=\"d-flex a-i-center p-t-4\"\n for=\"dateSelection\"\n >\n {{ 'Date selection' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"popoverTemplate\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n </label>\n <ng-template #popoverTemplate>\n <span translate>\n Choose how to select a date range, the available options are:\n <ul class=\"m-l-0 p-l-8 m-t-8 m-b-0\">\n <!-- Will be re-enabled in MTM-61920 -->\n <!-- <li>\n <b>Dashboard time range:</b>\n restricts date selection to the global dashboard configuration only\n </li> -->\n <li>\n <b>Widget configuration:</b>\n restricts the date selection only to the widget configuration\n </li>\n <li>\n <b>Widget and widget configuration:</b>\n restricts the date selection to the widget view and widget configuration only\n </li>\n </ul>\n </span>\n </ng-template>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control text-12\"\n [title]=\"'Date selection' | translate\"\n [attr.aria-label]=\"'Date selection' | translate\"\n id=\"globalDateSelector\"\n formControlName=\"globalDateSelector\"\n >\n <option\n *ngFor=\"let dataSelectionValue of DATE_SELECTION_VALUES_ARR\"\n [ngValue]=\"dataSelectionValue\"\n >\n {{ DATE_SELECTION_LABELS[dataSelectionValue] | translate }}\n </option>\n </select>\n </div>\n </div>\n </div>\n <div class=\"row\">\n <c8y-datapoint-selection-list\n class=\"bg-inherit separator-top p-t-16 d-block\"\n listTitle=\"{{ 'Data points' | translate }}\"\n name=\"datapoints\"\n [defaultFormOptions]=\"defaultFormOptions\"\n [config]=\"datapointSelectionConfig\"\n [minActiveCount]=\"1\"\n formControlName=\"datapoints\"\n ></c8y-datapoint-selection-list>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"row\">\n <ng-container *ngIf=\"!isWidgetLinkedToGlobalTimeContext\">\n <div class=\"col-md-6\">\n <!-- interval selector -->\n <fieldset class=\"c8y-fieldset\">\n <legend class=\"d-flex a-i-center\">\n {{ 'Auto refresh' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"\n 'Change the state of interval automatic refresh and set the refresh frequency.'\n | translate\n \"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n </legend>\n <c8y-form-group class=\"m-b-16 form-group-sm\">\n <div class=\"d-flex gap-4 m-t-8 m-b-8 a-i-center\">\n <label class=\"c8y-switch\">\n <input\n id=\"refreshToggle\"\n name=\"isAutoRefreshEnabled\"\n type=\"checkbox\"\n formControlName=\"isAutoRefreshEnabled\"\n (click)=\"toggleRefreshIntervalControl()\"\n />\n <span></span>\n <span class=\"sr-only\">{{ 'Auto refresh' | translate }}</span>\n </label>\n <label\n class=\"m-b-0\"\n for=\"refreshInterval\"\n >\n {{ 'Interval' | translate }}\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control text-12\"\n [title]=\"'Refresh interval in seconds' | translate\"\n id=\"refreshInterval\"\n formControlName=\"refreshInterval\"\n >\n <option\n *ngFor=\"let refreshInterval of REFRESH_INTERVAL_VALUES_ARR\"\n [ngValue]=\"refreshInterval\"\n >\n {{ '{{ seconds }} s' | translate: { seconds: refreshInterval / 1000 } }}\n </option>\n </select>\n </div>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n </ng-container>\n <!-- decimal input -->\n <div class=\"col-md-6\">\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Decimal places' | translate }}\n </legend>\n <c8y-form-group class=\"p-t-8\">\n <input\n class=\"form-control\"\n name=\"decimalPlaces\"\n type=\"number\"\n formControlName=\"decimalPlaces\"\n step=\"1\"\n />\n </c8y-form-group>\n </fieldset>\n </div>\n </div>\n <!-- aggregation selector -->\n <ng-container *ngIf=\"!isWidgetLinkedToGlobalTimeContext\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Aggregation' | translate }}\n </legend>\n <c8y-form-group class=\"p-t-8\">\n <div class=\"c8y-select-wrapper\">\n <!-- Setting below [attr.disabled] ensures that the control is visually disabled and user interaction is prevented,\n while still allowing the value to be updated and saved correctly in the form.\n This solution covers the case where the user enables auto-refresh, in which case aggregation must be set to NONE. -->\n <select\n class=\"form-control text-12\"\n [title]=\"'Aggregation' | translate\"\n id=\"aggregation\"\n formControlName=\"aggregation\"\n [attr.disabled]=\"formGroup.value.isAutoRefreshEnabled ? true : null\"\n >\n <option\n *ngFor=\"let aggregationValue of AGGREGATION_VALUES_ARR\"\n [ngValue]=\"aggregationValue\"\n [disabled]=\"disabledAggregationOptions[aggregationValue]\"\n >\n {{ AGGREGATION_LABELS[aggregationValue] | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n <!-- time interval selector -->\n <div class=\"col-md-6\">\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Time interval' | translate }}\n </legend>\n <c8y-form-group class=\"p-t-8\">\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control text-12\"\n [title]=\"'Interval' | translate\"\n id=\"interval\"\n formControlName=\"interval\"\n >\n <option\n *ngFor=\"let intervalValue of INTERVAL_VALUES_ARR\"\n [ngValue]=\"intervalValue\"\n >\n {{ TIME_RANGE_INTERVAL_LABELS[intervalValue] | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n </div>\n <!-- date pickers -->\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Date range' | translate }}\n </legend>\n <c8y-date-range-picker></c8y-date-range-picker>\n </fieldset>\n </ng-container>\n </div>\n </form>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "pipe", type: i1$1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i1$1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i1$1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1$1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: DatapointSelectorModule }, { kind: "component", type: i6.DatapointSelectionListComponent, selector: "c8y-datapoint-selection-list", inputs: ["actions", "allowDragAndDrop", "config", "defaultFormOptions", "maxActiveCount", "minActiveCount", "resolveContext", "listTitle"], outputs: ["isValid", "change"] }, { kind: "component", type: DateRangePickerComponent, selector: "c8y-date-range-picker", inputs: ["isEmittingDateChange", "showLabel"], outputs: ["updatedDate"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i7.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "ngmodule", type: ReactiveFormsModule }] }); }
|
|
513
538
|
}
|
|
514
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type:
|
|
515
|
-
type:
|
|
516
|
-
args: [{
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
539
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableWidgetConfigComponent, decorators: [{
|
|
540
|
+
type: Component,
|
|
541
|
+
args: [{ selector: 'c8y-datapoints-table-view-config', standalone: true, imports: [
|
|
542
|
+
CoreModule,
|
|
543
|
+
DatapointSelectorModule,
|
|
544
|
+
DateRangePickerComponent,
|
|
545
|
+
PopoverModule,
|
|
546
|
+
ReactiveFormsModule
|
|
547
|
+
], template: "<div class=\"p-l-24 p-r-24\">\n <form\n class=\"no-card-context\"\n [formGroup]=\"formGroup\"\n >\n <!-- <datapoints-selector> -->\n <div class=\"col-md-6\">\n <div class=\"row\">\n <!-- global-time-context-selector -->\n <div class=\"form-group m-b-0 p-b-16 separator-bottom\">\n <label\n class=\"d-flex a-i-center p-t-4\"\n for=\"dateSelection\"\n >\n {{ 'Date selection' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"popoverTemplate\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n </label>\n <ng-template #popoverTemplate>\n <span translate>\n Choose how to select a date range, the available options are:\n <ul class=\"m-l-0 p-l-8 m-t-8 m-b-0\">\n <!-- Will be re-enabled in MTM-61920 -->\n <!-- <li>\n <b>Dashboard time range:</b>\n restricts date selection to the global dashboard configuration only\n </li> -->\n <li>\n <b>Widget configuration:</b>\n restricts the date selection only to the widget configuration\n </li>\n <li>\n <b>Widget and widget configuration:</b>\n restricts the date selection to the widget view and widget configuration only\n </li>\n </ul>\n </span>\n </ng-template>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control text-12\"\n [title]=\"'Date selection' | translate\"\n [attr.aria-label]=\"'Date selection' | translate\"\n id=\"globalDateSelector\"\n formControlName=\"globalDateSelector\"\n >\n <option\n *ngFor=\"let dataSelectionValue of DATE_SELECTION_VALUES_ARR\"\n [ngValue]=\"dataSelectionValue\"\n >\n {{ DATE_SELECTION_LABELS[dataSelectionValue] | translate }}\n </option>\n </select>\n </div>\n </div>\n </div>\n <div class=\"row\">\n <c8y-datapoint-selection-list\n class=\"bg-inherit separator-top p-t-16 d-block\"\n listTitle=\"{{ 'Data points' | translate }}\"\n name=\"datapoints\"\n [defaultFormOptions]=\"defaultFormOptions\"\n [config]=\"datapointSelectionConfig\"\n [minActiveCount]=\"1\"\n formControlName=\"datapoints\"\n ></c8y-datapoint-selection-list>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"row\">\n <ng-container *ngIf=\"!isWidgetLinkedToGlobalTimeContext\">\n <div class=\"col-md-6\">\n <!-- interval selector -->\n <fieldset class=\"c8y-fieldset\">\n <legend class=\"d-flex a-i-center\">\n {{ 'Auto refresh' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"\n 'Change the state of interval automatic refresh and set the refresh frequency.'\n | translate\n \"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n </legend>\n <c8y-form-group class=\"m-b-16 form-group-sm\">\n <div class=\"d-flex gap-4 m-t-8 m-b-8 a-i-center\">\n <label class=\"c8y-switch\">\n <input\n id=\"refreshToggle\"\n name=\"isAutoRefreshEnabled\"\n type=\"checkbox\"\n formControlName=\"isAutoRefreshEnabled\"\n (click)=\"toggleRefreshIntervalControl()\"\n />\n <span></span>\n <span class=\"sr-only\">{{ 'Auto refresh' | translate }}</span>\n </label>\n <label\n class=\"m-b-0\"\n for=\"refreshInterval\"\n >\n {{ 'Interval' | translate }}\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control text-12\"\n [title]=\"'Refresh interval in seconds' | translate\"\n id=\"refreshInterval\"\n formControlName=\"refreshInterval\"\n >\n <option\n *ngFor=\"let refreshInterval of REFRESH_INTERVAL_VALUES_ARR\"\n [ngValue]=\"refreshInterval\"\n >\n {{ '{{ seconds }} s' | translate: { seconds: refreshInterval / 1000 } }}\n </option>\n </select>\n </div>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n </ng-container>\n <!-- decimal input -->\n <div class=\"col-md-6\">\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Decimal places' | translate }}\n </legend>\n <c8y-form-group class=\"p-t-8\">\n <input\n class=\"form-control\"\n name=\"decimalPlaces\"\n type=\"number\"\n formControlName=\"decimalPlaces\"\n step=\"1\"\n />\n </c8y-form-group>\n </fieldset>\n </div>\n </div>\n <!-- aggregation selector -->\n <ng-container *ngIf=\"!isWidgetLinkedToGlobalTimeContext\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Aggregation' | translate }}\n </legend>\n <c8y-form-group class=\"p-t-8\">\n <div class=\"c8y-select-wrapper\">\n <!-- Setting below [attr.disabled] ensures that the control is visually disabled and user interaction is prevented,\n while still allowing the value to be updated and saved correctly in the form.\n This solution covers the case where the user enables auto-refresh, in which case aggregation must be set to NONE. -->\n <select\n class=\"form-control text-12\"\n [title]=\"'Aggregation' | translate\"\n id=\"aggregation\"\n formControlName=\"aggregation\"\n [attr.disabled]=\"formGroup.value.isAutoRefreshEnabled ? true : null\"\n >\n <option\n *ngFor=\"let aggregationValue of AGGREGATION_VALUES_ARR\"\n [ngValue]=\"aggregationValue\"\n [disabled]=\"disabledAggregationOptions[aggregationValue]\"\n >\n {{ AGGREGATION_LABELS[aggregationValue] | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n <!-- time interval selector -->\n <div class=\"col-md-6\">\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Time interval' | translate }}\n </legend>\n <c8y-form-group class=\"p-t-8\">\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control text-12\"\n [title]=\"'Interval' | translate\"\n id=\"interval\"\n formControlName=\"interval\"\n >\n <option\n *ngFor=\"let intervalValue of INTERVAL_VALUES_ARR\"\n [ngValue]=\"intervalValue\"\n >\n {{ TIME_RANGE_INTERVAL_LABELS[intervalValue] | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n </div>\n <!-- date pickers -->\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Date range' | translate }}\n </legend>\n <c8y-date-range-picker></c8y-date-range-picker>\n </fieldset>\n </ng-container>\n </div>\n </form>\n</div>\n" }]
|
|
548
|
+
}], ctorParameters: () => [{ type: i1$1.AggregationService }, { type: DatapointsTableService }, { type: i3.FormBuilder }, { type: i3.NgForm }, { type: i4.WidgetConfigComponent }], propDecorators: { config: [{
|
|
549
|
+
type: Input
|
|
550
|
+
}] } });
|
|
520
551
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
552
|
+
/**
|
|
553
|
+
* A pipe that adjusts the aggregated time range based on the aggregation type.
|
|
554
|
+
*
|
|
555
|
+
* ```html
|
|
556
|
+
* '9:00' | adjustAggregatedTimeRange: config.aggregation (e.g.:HOURLY)
|
|
557
|
+
* ```
|
|
558
|
+
* The output will be '9:00-10:00'.
|
|
559
|
+
*/
|
|
560
|
+
class AdjustAggregatedTimeRangePipe {
|
|
561
|
+
/**
|
|
562
|
+
* Transforms the input time based on the aggregation type.
|
|
563
|
+
* @param inputTime The input time string.
|
|
564
|
+
* @param aggregationType The type of aggregation (optional).
|
|
565
|
+
* @returns The transformed time string.
|
|
566
|
+
*/
|
|
567
|
+
transform(inputTime, aggregationType$1) {
|
|
568
|
+
if (!aggregationType$1) {
|
|
569
|
+
return inputTime;
|
|
570
|
+
}
|
|
571
|
+
if (aggregationType$1 === aggregationType.DAILY) {
|
|
572
|
+
return '';
|
|
573
|
+
}
|
|
574
|
+
const date = this.createDateFromInput(inputTime);
|
|
575
|
+
const isTwelveHoursFormat = this.isTwelveHoursFormat(inputTime);
|
|
576
|
+
switch (aggregationType$1) {
|
|
577
|
+
case aggregationType.HOURLY:
|
|
578
|
+
return this.getHourlyTimeRange(date, isTwelveHoursFormat);
|
|
579
|
+
case aggregationType.MINUTELY:
|
|
580
|
+
return this.getMinutelyTimeRange(date, isTwelveHoursFormat);
|
|
550
581
|
default:
|
|
551
|
-
throw new Error('
|
|
582
|
+
throw new Error('Unsupported aggregation type');
|
|
552
583
|
}
|
|
553
|
-
return {
|
|
554
|
-
dateFrom: dateFrom,
|
|
555
|
-
dateTo: this.datapointsTableViewService.adjustDate(nowString, 0, true)
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableService, deps: [{ token: DatapointsTableViewService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
559
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableService, providedIn: 'root' }); }
|
|
560
|
-
}
|
|
561
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableService, decorators: [{
|
|
562
|
-
type: Injectable,
|
|
563
|
-
args: [{
|
|
564
|
-
providedIn: 'root'
|
|
565
|
-
}]
|
|
566
|
-
}], ctorParameters: () => [{ type: DatapointsTableViewService }] });
|
|
567
|
-
|
|
568
|
-
class DateRangePickerComponent {
|
|
569
|
-
constructor() {
|
|
570
|
-
/**
|
|
571
|
-
* If set to true, the component will be reactive and will emit the updatedDate on every change.
|
|
572
|
-
* Otherwise, the component will use a non emitting variant of a template.
|
|
573
|
-
*/
|
|
574
|
-
this.isEmittingDateChange = false;
|
|
575
|
-
/**
|
|
576
|
-
* Determines the display of from and to date picker labels.
|
|
577
|
-
*/
|
|
578
|
-
this.showLabel = true;
|
|
579
|
-
this.updatedDate = new EventEmitter();
|
|
580
|
-
this.DATE_FROM = 'dateFrom';
|
|
581
|
-
this.DATE_TO = 'dateTo';
|
|
582
|
-
this.FROM_DATE = gettext('From`date`');
|
|
583
|
-
this.HAS_ERROR = 'has-error';
|
|
584
|
-
this.INVALID_DATE_TIME = 'invalidDateTime';
|
|
585
|
-
this.THIS_DATE_IS_INVALID = gettext('This date is invalid.');
|
|
586
|
-
this.THIS_DATE_IS_AFTER_THE_LAST_ALLOWED_DATE = gettext('This date is after the latest allowed date.');
|
|
587
|
-
this.THIS_DATE_IS_BEFORE_THE_EARLIEST_ALLOWED_DATE = gettext('This date is before the earliest allowed date.');
|
|
588
|
-
this.TO_DATE = gettext('To`date`');
|
|
589
|
-
this.parentContainer = inject(ControlContainer);
|
|
590
|
-
}
|
|
591
|
-
onDateFromChange(dateFrom) {
|
|
592
|
-
this.updatedDate.emit({
|
|
593
|
-
dateFrom
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
onDateToChange(dateTo) {
|
|
597
|
-
this.updatedDate.emit({
|
|
598
|
-
dateTo
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
get parentFormGroup() {
|
|
602
|
-
return this.parentContainer.control;
|
|
603
|
-
}
|
|
604
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DateRangePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
605
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DateRangePickerComponent, isStandalone: true, selector: "c8y-date-range-picker", inputs: { isEmittingDateChange: "isEmittingDateChange", showLabel: "showLabel" }, outputs: { updatedDate: "updatedDate" }, ngImport: i0, template: "<div class=\"d-flex gap-8 a-i-center flex-wrap\">\n <c8y-form-group\n [ngClass]=\"[\n parentFormGroup?.controls.dateFrom.errors ? HAS_ERROR : '',\n isEmittingDateChange ? 'd-flex a-i-center gap-4 m-b-0' : ''\n ]\"\n >\n <ng-container *ngIf=\"showLabel\">\n <label\n [title]=\"FROM_DATE | translate\"\n [for]=\"DATE_FROM\"\n >\n {{ FROM_DATE | translate }}\n </label>\n </ng-container>\n <c8y-date-time-picker\n id=\"DATE_FROM\"\n [maxDate]=\"parentFormGroup?.value.dateTo\"\n [placeholder]=\"FROM_DATE | translate\"\n [formControl]=\"parentFormGroup?.controls.dateFrom\"\n [ngClass]=\"parentFormGroup?.controls.dateFrom.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"onDateFromChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages\n class=\"text-nowrap\"\n [show]=\"parentFormGroup?.controls.dateFrom.errors\"\n >\n <c8y-message\n name=\"dateAfterRangeMax\"\n [text]=\"THIS_DATE_IS_AFTER_THE_LAST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n <c8y-form-group\n [ngClass]=\"[\n parentFormGroup?.controls.dateTo.errors ? HAS_ERROR : '',\n isEmittingDateChange ? 'd-flex a-i-center gap-4 m-b-0' : ''\n ]\"\n >\n <ng-container *ngIf=\"showLabel\">\n <label\n [title]=\"TO_DATE | translate\"\n [for]=\"DATE_TO\"\n >\n {{ TO_DATE | translate }}\n </label>\n </ng-container>\n <c8y-date-time-picker\n id=\"DATE_TO\"\n [minDate]=\"parentFormGroup?.value.dateFrom\"\n [placeholder]=\"TO_DATE | translate\"\n [formControl]=\"parentFormGroup?.controls.dateTo\"\n [ngClass]=\"parentFormGroup?.controls.dateTo.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"onDateToChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"parentFormGroup?.controls.dateTo.errors\">\n <c8y-message\n name=\"dateBeforeRangeMin\"\n [text]=\"THIS_DATE_IS_BEFORE_THE_EARLIEST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "pipe", type: i1$1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "component", type: i1$1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1$1.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i1$1.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: i1$1.DateTimePickerComponent, selector: "c8y-date-time-picker", inputs: ["minDate", "maxDate", "placeholder", "dateInputFormat", "adaptivePosition", "size", "dateType", "config"], outputs: ["onDateSelected"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }], viewProviders: [
|
|
606
|
-
{
|
|
607
|
-
provide: ControlContainer,
|
|
608
|
-
useFactory: () => inject(ControlContainer, { skipSelf: true })
|
|
609
|
-
}
|
|
610
|
-
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
611
|
-
}
|
|
612
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DateRangePickerComponent, decorators: [{
|
|
613
|
-
type: Component,
|
|
614
|
-
args: [{ selector: 'c8y-date-range-picker', standalone: true, imports: [CoreModule, CommonModule, ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.OnPush, viewProviders: [
|
|
615
|
-
{
|
|
616
|
-
provide: ControlContainer,
|
|
617
|
-
useFactory: () => inject(ControlContainer, { skipSelf: true })
|
|
618
|
-
}
|
|
619
|
-
], template: "<div class=\"d-flex gap-8 a-i-center flex-wrap\">\n <c8y-form-group\n [ngClass]=\"[\n parentFormGroup?.controls.dateFrom.errors ? HAS_ERROR : '',\n isEmittingDateChange ? 'd-flex a-i-center gap-4 m-b-0' : ''\n ]\"\n >\n <ng-container *ngIf=\"showLabel\">\n <label\n [title]=\"FROM_DATE | translate\"\n [for]=\"DATE_FROM\"\n >\n {{ FROM_DATE | translate }}\n </label>\n </ng-container>\n <c8y-date-time-picker\n id=\"DATE_FROM\"\n [maxDate]=\"parentFormGroup?.value.dateTo\"\n [placeholder]=\"FROM_DATE | translate\"\n [formControl]=\"parentFormGroup?.controls.dateFrom\"\n [ngClass]=\"parentFormGroup?.controls.dateFrom.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"onDateFromChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages\n class=\"text-nowrap\"\n [show]=\"parentFormGroup?.controls.dateFrom.errors\"\n >\n <c8y-message\n name=\"dateAfterRangeMax\"\n [text]=\"THIS_DATE_IS_AFTER_THE_LAST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n <c8y-form-group\n [ngClass]=\"[\n parentFormGroup?.controls.dateTo.errors ? HAS_ERROR : '',\n isEmittingDateChange ? 'd-flex a-i-center gap-4 m-b-0' : ''\n ]\"\n >\n <ng-container *ngIf=\"showLabel\">\n <label\n [title]=\"TO_DATE | translate\"\n [for]=\"DATE_TO\"\n >\n {{ TO_DATE | translate }}\n </label>\n </ng-container>\n <c8y-date-time-picker\n id=\"DATE_TO\"\n [minDate]=\"parentFormGroup?.value.dateFrom\"\n [placeholder]=\"TO_DATE | translate\"\n [formControl]=\"parentFormGroup?.controls.dateTo\"\n [ngClass]=\"parentFormGroup?.controls.dateTo.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"onDateToChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"parentFormGroup?.controls.dateTo.errors\">\n <c8y-message\n name=\"dateBeforeRangeMin\"\n [text]=\"THIS_DATE_IS_BEFORE_THE_EARLIEST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n</div>\n" }]
|
|
620
|
-
}], propDecorators: { isEmittingDateChange: [{
|
|
621
|
-
type: Input
|
|
622
|
-
}], showLabel: [{
|
|
623
|
-
type: Input
|
|
624
|
-
}], updatedDate: [{
|
|
625
|
-
type: Output
|
|
626
|
-
}] } });
|
|
627
|
-
|
|
628
|
-
class DatapointsTableConfigService {
|
|
629
|
-
/**
|
|
630
|
-
* Determines the interval between two dates.
|
|
631
|
-
*
|
|
632
|
-
* @param dateFrom - The starting date in ISO 8601 string format.
|
|
633
|
-
* @param dateTo - The ending date in ISO 8601 string format.
|
|
634
|
-
* @returns The time range interval unit.
|
|
635
|
-
*/
|
|
636
|
-
// TODO same method is in datapoints export component. Consider moving it to a shared service, or reuse it here
|
|
637
|
-
// when DPT will be integrating with MTM-59689.
|
|
638
|
-
// Integration task: MTM-60221
|
|
639
|
-
// tests are already added in MTM-60217
|
|
640
|
-
determineInterval(dateFrom, dateTo) {
|
|
641
|
-
const from = new Date(dateFrom);
|
|
642
|
-
const to = new Date(dateTo);
|
|
643
|
-
const differenceInMilliseconds = to.getTime() - from.getTime();
|
|
644
|
-
const [minutes, hours, days, weeks, months, custom] = INTERVALS;
|
|
645
|
-
const intervals = [
|
|
646
|
-
{ value: minutes.timespanInMs, id: minutes.id }, // milliseconds in one minute
|
|
647
|
-
{ value: hours.timespanInMs, id: hours.id }, // milliseconds in one hour
|
|
648
|
-
{ value: days.timespanInMs, id: days.id }, // milliseconds in one day
|
|
649
|
-
{ value: weeks.timespanInMs, id: weeks.id }, // milliseconds in one week
|
|
650
|
-
{ value: months.timespanInMs, id: months.id } // approximation for milliseconds in one month
|
|
651
|
-
];
|
|
652
|
-
const interval = intervals.find(interval => differenceInMilliseconds <= interval.value);
|
|
653
|
-
return interval ? interval.id : custom.id;
|
|
654
584
|
}
|
|
655
585
|
/**
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
*
|
|
659
|
-
* - Retrieves the current date range from the form controls.
|
|
660
|
-
* - Determines the current time interval based on the date range.
|
|
661
|
-
* - Sets the disabled state for each aggregation option based on predefined conditions.
|
|
662
|
-
*
|
|
663
|
-
* The disabled state is stored in the `disabledAggregationOptions` object,
|
|
664
|
-
* where the key is the aggregation value and the value is a boolean indicating whether the option should be disabled.
|
|
665
|
-
*
|
|
666
|
-
* The `disabledConditions` object defines the conditions under which each aggregation option should be disabled.
|
|
667
|
-
*
|
|
668
|
-
* @param dateFrom - The starting date of the range.
|
|
669
|
-
* @param dateTo - The ending date of the range.
|
|
670
|
-
* @param aggregationValuesArr - An array of aggregation options.
|
|
671
|
-
* @returns An object containing the disabled state of each aggregation option.
|
|
672
|
-
*/
|
|
673
|
-
// TODO similar method is in datapoints export component. Consider moving it to a shared service when DPT will be integrating with MTM-59689.
|
|
674
|
-
// Integration task: MTM-60221
|
|
675
|
-
// tests are already added in MTM-60217
|
|
676
|
-
updateDisabledStateOfAggregationOptionEntries(dateFrom, dateTo, aggregationValuesArr) {
|
|
677
|
-
const currentInterval = this.determineInterval(dateFrom, dateTo);
|
|
678
|
-
const disabledConditions = {
|
|
679
|
-
[AGGREGATION_VALUES.daily]: [
|
|
680
|
-
TIME_RANGE_INTERVAL_UNITS_VALUES.minutes,
|
|
681
|
-
TIME_RANGE_INTERVAL_UNITS_VALUES.hours,
|
|
682
|
-
TIME_RANGE_INTERVAL_UNITS_VALUES.days
|
|
683
|
-
],
|
|
684
|
-
[AGGREGATION_VALUES.hourly]: [
|
|
685
|
-
TIME_RANGE_INTERVAL_UNITS_VALUES.minutes,
|
|
686
|
-
TIME_RANGE_INTERVAL_UNITS_VALUES.hours
|
|
687
|
-
],
|
|
688
|
-
[AGGREGATION_VALUES.minutely]: [TIME_RANGE_INTERVAL_UNITS_VALUES.minutes]
|
|
689
|
-
};
|
|
690
|
-
const disabledAggregationOptions = {};
|
|
691
|
-
for (const aggregationValue of aggregationValuesArr) {
|
|
692
|
-
disabledAggregationOptions[aggregationValue] =
|
|
693
|
-
disabledConditions[aggregationValue]?.includes(currentInterval) ?? false;
|
|
694
|
-
}
|
|
695
|
-
return disabledAggregationOptions;
|
|
696
|
-
}
|
|
697
|
-
/**
|
|
698
|
-
* Determines the new aggregation value based on the current value and disabled options.
|
|
699
|
-
*
|
|
700
|
-
* Goal is to switch to the next available aggregation option if the current one is disabled.
|
|
701
|
-
* - If the current option is disabled, sets the control to the first available (non-disabled) option based on the following order:
|
|
702
|
-
* - If the current value is `DAILY`, it switches to `HOURLY` if it's not disabled, otherwise to `MINUTELY` if `HOURLY` is also disabled.
|
|
703
|
-
* - If the current value is `HOURLY`, it switches to `MINUTELY` if it's not disabled.
|
|
704
|
-
* - If all options are disabled, it sets the value to `NONE`.
|
|
705
|
-
*
|
|
706
|
-
* @param currentValue - The current aggregation option.
|
|
707
|
-
* @param disabledOptions - An object containing disabled options.
|
|
708
|
-
* @returns The new aggregation option.
|
|
586
|
+
* Creates a date object from the input time string.
|
|
587
|
+
* @param inputTime The input time string.
|
|
588
|
+
* @returns The created Date object.
|
|
709
589
|
*/
|
|
710
|
-
|
|
711
|
-
const
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
return !disabledOptions[minutely] ? minutely : none;
|
|
590
|
+
createDateFromInput(inputTime) {
|
|
591
|
+
const defaultDate = '1970-01-01 ';
|
|
592
|
+
const isPM = /PM/i.test(inputTime);
|
|
593
|
+
const cleanedTime = inputTime.replace(/AM|PM/i, '').trim();
|
|
594
|
+
this.validateTimeFormat(cleanedTime, inputTime);
|
|
595
|
+
const dateTimeString = `${defaultDate}${cleanedTime}`;
|
|
596
|
+
const date = new Date(dateTimeString);
|
|
597
|
+
if (isNaN(date.getTime())) {
|
|
598
|
+
throw new Error('Invalid input time');
|
|
720
599
|
}
|
|
721
|
-
return
|
|
600
|
+
return this.adjustForPMTime(date, isPM);
|
|
722
601
|
}
|
|
723
602
|
/**
|
|
724
|
-
*
|
|
725
|
-
*
|
|
726
|
-
* @param
|
|
727
|
-
* @
|
|
603
|
+
* Validates if the time string matches the required format and has valid values.
|
|
604
|
+
* @param time The time string to validate.
|
|
605
|
+
* @param originalInput The original input string (including AM/PM if present).
|
|
606
|
+
* @throws Error if the time format is invalid or values are out of range.
|
|
728
607
|
*/
|
|
729
|
-
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
};
|
|
735
|
-
|
|
608
|
+
validateTimeFormat(time, originalInput) {
|
|
609
|
+
const parts = time.split(':');
|
|
610
|
+
this.validateTimeParts(parts);
|
|
611
|
+
const [hoursStr, minutesStr, secondsStr] = parts;
|
|
612
|
+
this.validateTimeDigits(hoursStr, minutesStr, secondsStr);
|
|
613
|
+
const { hours, minutes, seconds } = this.parseTimeComponents(hoursStr, minutesStr, secondsStr);
|
|
614
|
+
this.validateTimeRanges(hours, minutes, seconds);
|
|
615
|
+
this.validateTimeFormat24Hour(hours, originalInput);
|
|
736
616
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableConfigService, decorators: [{
|
|
741
|
-
type: Injectable,
|
|
742
|
-
args: [{
|
|
743
|
-
providedIn: 'root'
|
|
744
|
-
}]
|
|
745
|
-
}] });
|
|
746
|
-
|
|
747
|
-
function minOneDatapointActive() {
|
|
748
|
-
return (control) => {
|
|
749
|
-
const datapoints = control.value;
|
|
750
|
-
if (!datapoints || !datapoints.length) {
|
|
751
|
-
return null;
|
|
752
|
-
}
|
|
753
|
-
const activeDatapoints = datapoints.filter(datapoint => datapoint.__active);
|
|
754
|
-
if (activeDatapoints.length >= 1) {
|
|
755
|
-
return null;
|
|
617
|
+
validateTimeParts(parts) {
|
|
618
|
+
if (parts.length < 2 || parts.length > 3) {
|
|
619
|
+
throw new Error('Invalid input time');
|
|
756
620
|
}
|
|
757
|
-
return { exactlyOneDatapointNeedsToBeActive: true };
|
|
758
|
-
};
|
|
759
|
-
}
|
|
760
|
-
class DatapointsTableWidgetConfigComponent {
|
|
761
|
-
constructor(datapointsTableService, datapointsTableConfigService, formBuilder, form, widgetConfig) {
|
|
762
|
-
this.datapointsTableService = datapointsTableService;
|
|
763
|
-
this.datapointsTableConfigService = datapointsTableConfigService;
|
|
764
|
-
this.formBuilder = formBuilder;
|
|
765
|
-
this.form = form;
|
|
766
|
-
this.widgetConfig = widgetConfig;
|
|
767
|
-
this.AGGREGATION_LABELS = AGGREGATION_LABELS;
|
|
768
|
-
this.DATE_SELECTION_LABELS = DATE_SELECTION_LABELS;
|
|
769
|
-
// Will be uncommented in MTM-61920
|
|
770
|
-
// readonly DEFAULT_DATE_SELECTOR_VALUE = DATE_SELECTION_VALUES.dashboard_context;
|
|
771
|
-
this.DEFAULT_DATE_SELECTOR_VALUE = DATE_SELECTION_VALUES.config;
|
|
772
|
-
this.DEFAULT_INTERVAL_VALUE = TIME_RANGE_INTERVAL_UNITS_VALUES.hours;
|
|
773
|
-
this.TIME_RANGE_INTERVAL_LABELS = TIME_RANGE_INTERVAL_LABELS;
|
|
774
|
-
this.AGGREGATION_VALUES_ARR = AGGREGATION_VALUES_ARR;
|
|
775
|
-
this.DATE_SELECTION_VALUES_ARR = DATE_SELECTION_VALUES_ARR;
|
|
776
|
-
this.INTERVAL_VALUES_ARR = INTERVAL_VALUES_ARR;
|
|
777
|
-
this.REFRESH_INTERVAL_VALUES_ARR = REFRESH_INTERVAL_VALUES_ARR;
|
|
778
|
-
this.datapointSelectionConfig = {};
|
|
779
|
-
this.disabledAggregationOptions = {};
|
|
780
|
-
this.defaultFormOptions = {
|
|
781
|
-
selectableChartLineTypes: [],
|
|
782
|
-
selectableAxisTypes: [],
|
|
783
|
-
showRedRange: true,
|
|
784
|
-
showYellowRange: true
|
|
785
|
-
};
|
|
786
|
-
this.decimalLimits = {
|
|
787
|
-
numberOfDecimalPlacesMin: 0,
|
|
788
|
-
numberOfDecimalPlacesMax: 10
|
|
789
|
-
};
|
|
790
|
-
/**
|
|
791
|
-
* Indicate when the time interval selector item has been changed programmatically.
|
|
792
|
-
*
|
|
793
|
-
* This property is used to track changes in the time interval selector
|
|
794
|
-
* that are not triggered by direct user interaction, but by the application itself.
|
|
795
|
-
*
|
|
796
|
-
* In our case, the date selector and the interval selector are linked.
|
|
797
|
-
* So, when one of them changes, it affects the other.
|
|
798
|
-
* For example, selecting "Last hour" in the interval selector should automatically update the date range to the last hour.
|
|
799
|
-
* But without this flag, changing the date range would also update the interval selector, resulting in "Custom date" being selected.
|
|
800
|
-
* This happens because the system would interpret the behavior this way.
|
|
801
|
-
*/
|
|
802
|
-
this.isIntervalSelectorChangedProgrammatically = false;
|
|
803
|
-
this.destroy$ = new Subject();
|
|
804
621
|
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
this.
|
|
809
|
-
|
|
810
|
-
if (this.widgetConfig.context?.id) {
|
|
811
|
-
this.datapointSelectionConfig.contextAsset = this.widgetConfig?.context;
|
|
622
|
+
validateTimeDigits(hoursStr, minutesStr, secondsStr) {
|
|
623
|
+
if (!this.isValidNumberString(hoursStr) ||
|
|
624
|
+
!this.isValidNumberString(minutesStr) ||
|
|
625
|
+
(secondsStr !== undefined && !this.isValidNumberString(secondsStr))) {
|
|
626
|
+
throw new Error('Invalid input time');
|
|
812
627
|
}
|
|
813
|
-
this.isWidgetLinkedToGlobalTimeContext = this.config.widgetInstanceGlobalTimeContext;
|
|
814
|
-
this.initForm();
|
|
815
|
-
this.handleAutoRefreshToggleChanges();
|
|
816
|
-
this.handleIntervalSelectorChanges();
|
|
817
|
-
this.handleDateSelectorChanges();
|
|
818
|
-
this.handleGlobalDateSelectorChanges();
|
|
819
|
-
this.disabledAggregationOptions =
|
|
820
|
-
this.datapointsTableConfigService.updateDisabledStateOfAggregationOptionEntries(this.formGroup.controls.dateFrom.value, this.formGroup.controls.dateTo.value, AGGREGATION_VALUES_ARR);
|
|
821
628
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
629
|
+
parseTimeComponents(hoursStr, minutesStr, secondsStr) {
|
|
630
|
+
return {
|
|
631
|
+
hours: Number(hoursStr),
|
|
632
|
+
minutes: Number(minutesStr),
|
|
633
|
+
seconds: secondsStr ? Number(secondsStr) : 0
|
|
634
|
+
};
|
|
825
635
|
}
|
|
826
|
-
|
|
827
|
-
if (
|
|
828
|
-
|
|
829
|
-
// The 'NONE' aggregation type is not handled by a backend, so it simply needs to be deleted to get data without aggregation.
|
|
830
|
-
delete this.formGroup.value.aggregation;
|
|
831
|
-
delete config.aggregation;
|
|
636
|
+
validateTimeRanges(hours, minutes, seconds) {
|
|
637
|
+
if (hours > 23 || hours < 0 || minutes > 59 || minutes < 0 || seconds > 59 || seconds < 0) {
|
|
638
|
+
throw new Error('Invalid input time');
|
|
832
639
|
}
|
|
833
|
-
this.updateTimeContext(config, this.formGroup.value.globalDateSelector);
|
|
834
|
-
Object.assign(config, this.formGroup.value);
|
|
835
640
|
}
|
|
836
|
-
|
|
837
|
-
this.
|
|
838
|
-
|
|
839
|
-
: this.formGroup.controls.refreshInterval.enable();
|
|
840
|
-
}
|
|
841
|
-
updateTimeContext(config, selectedDateContext) {
|
|
842
|
-
switch (selectedDateContext) {
|
|
843
|
-
// Condition will be removed in MTM-61920
|
|
844
|
-
// case DATE_SELECTION_VALUES.dashboard_context:
|
|
845
|
-
// config.displayDateSelection = false;
|
|
846
|
-
// config.widgetInstanceGlobalTimeContext = true;
|
|
847
|
-
// break;
|
|
848
|
-
case DATE_SELECTION_VALUES.view_and_config:
|
|
849
|
-
config.displayDateSelection = true;
|
|
850
|
-
config.widgetInstanceGlobalTimeContext = false;
|
|
851
|
-
break;
|
|
852
|
-
case DATE_SELECTION_VALUES.config:
|
|
853
|
-
default:
|
|
854
|
-
config.displayDateSelection = false;
|
|
855
|
-
config.widgetInstanceGlobalTimeContext = false;
|
|
641
|
+
validateTimeFormat24Hour(hours, originalInput) {
|
|
642
|
+
if (hours > 12 && this.hasAmPm(originalInput)) {
|
|
643
|
+
throw new Error('Invalid input time');
|
|
856
644
|
}
|
|
857
645
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
646
|
+
/**
|
|
647
|
+
* Checks if string contains only digits and is 1-2 characters long.
|
|
648
|
+
* @param value String to check
|
|
649
|
+
* @returns boolean indicating if string is valid
|
|
650
|
+
*/
|
|
651
|
+
isValidNumberString(value) {
|
|
652
|
+
return (value.length > 0 &&
|
|
653
|
+
value.length <= 2 &&
|
|
654
|
+
value.split('').every(char => char >= '0' && char <= '9'));
|
|
866
655
|
}
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
}),
|
|
875
|
-
datapoints: this.formBuilder.control(new Array(), [
|
|
876
|
-
Validators.required,
|
|
877
|
-
Validators.minLength(1),
|
|
878
|
-
minOneDatapointActive()
|
|
879
|
-
]),
|
|
880
|
-
globalDateSelector: new FormControl(isLegacyWidget
|
|
881
|
-
? this.determineGlobalDateSelectorValue()
|
|
882
|
-
: this.DEFAULT_DATE_SELECTOR_VALUE),
|
|
883
|
-
dateFrom: new FormControl(dateFrom),
|
|
884
|
-
dateTo: new FormControl(dateTo),
|
|
885
|
-
decimalPlaces: [
|
|
886
|
-
2,
|
|
887
|
-
[
|
|
888
|
-
Validators.required,
|
|
889
|
-
Validators.min(this.decimalLimits.numberOfDecimalPlacesMin),
|
|
890
|
-
Validators.max(this.decimalLimits.numberOfDecimalPlacesMax),
|
|
891
|
-
Validators.pattern('^[0-9]+$')
|
|
892
|
-
]
|
|
893
|
-
],
|
|
894
|
-
interval: new FormControl(this.DEFAULT_INTERVAL_VALUE),
|
|
895
|
-
isAutoRefreshEnabled: isLegacyWidget ? this.config.realtime : [true],
|
|
896
|
-
refreshInterval: new FormControl({
|
|
897
|
-
value: DEFAULT_COUNTDOWN_VALUE,
|
|
898
|
-
disabled: this.isAutoRefershDisabled()
|
|
899
|
-
})
|
|
900
|
-
}, { validators: dateRangeValidator });
|
|
656
|
+
/**
|
|
657
|
+
* Checks if the input time has AM/PM markers.
|
|
658
|
+
* @param input The input time string to check.
|
|
659
|
+
* @returns boolean indicating if the input contains AM/PM.
|
|
660
|
+
*/
|
|
661
|
+
hasAmPm(input) {
|
|
662
|
+
return /AM|PM/i.test(input);
|
|
901
663
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
664
|
+
/**
|
|
665
|
+
* Adjusts the date for PM times by adding 12 hours when necessary.
|
|
666
|
+
* @param date The date object to adjust.
|
|
667
|
+
* @param isPM Boolean indicating if the time is PM.
|
|
668
|
+
* @returns The adjusted Date object.
|
|
669
|
+
*/
|
|
670
|
+
adjustForPMTime(date, isPM) {
|
|
671
|
+
const hours = date.getHours();
|
|
672
|
+
if (isPM && hours < 12) {
|
|
673
|
+
date.setHours(hours + 12);
|
|
905
674
|
}
|
|
906
|
-
if (!
|
|
907
|
-
|
|
675
|
+
else if (!isPM && hours === 12) {
|
|
676
|
+
date.setHours(0);
|
|
908
677
|
}
|
|
678
|
+
return date;
|
|
909
679
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
!this.config.refreshInterval &&
|
|
918
|
-
!this.config.isAutoRefreshEnabled;
|
|
919
|
-
if (isLegacyWidgetRealtimeNotActive) {
|
|
920
|
-
return true;
|
|
921
|
-
}
|
|
922
|
-
return !this.config.isAutoRefreshEnabled;
|
|
680
|
+
/**
|
|
681
|
+
* Checks if the input time is in twelve hours format.
|
|
682
|
+
* @param inputTime The input time string.
|
|
683
|
+
* @returns True if the input time is in twelve hours format, false otherwise.
|
|
684
|
+
*/
|
|
685
|
+
isTwelveHoursFormat(inputTime) {
|
|
686
|
+
return /AM|PM/i.test(inputTime);
|
|
923
687
|
}
|
|
924
688
|
/**
|
|
925
|
-
*
|
|
926
|
-
*
|
|
927
|
-
*
|
|
928
|
-
*
|
|
929
|
-
* visually disabled (but not programmatically disabled to ensure the value is saved).
|
|
930
|
-
* When auto-refresh is disabled, the aggregation control is enabled.
|
|
689
|
+
* Gets the hourly time range for the given date.
|
|
690
|
+
* @param date The date object.
|
|
691
|
+
* @param twelveHoursFormat Indicates whether to use twelve hours format.
|
|
692
|
+
* @returns The hourly time range string.
|
|
931
693
|
*/
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
const aggregationControl = this.formGroup.controls.aggregation;
|
|
937
|
-
if (isAutoRefreshEnabled) {
|
|
938
|
-
aggregationControl.setValue(AGGREGATION_VALUES.none);
|
|
939
|
-
// Do not disable the control programmatically to ensure the value is saved.
|
|
940
|
-
}
|
|
941
|
-
else {
|
|
942
|
-
aggregationControl.enable();
|
|
943
|
-
}
|
|
944
|
-
});
|
|
694
|
+
getHourlyTimeRange(date, twelveHoursFormat) {
|
|
695
|
+
const nextHour = new Date(date.getTime());
|
|
696
|
+
nextHour.setHours(date.getHours() + 1);
|
|
697
|
+
return `${this.formatTime(date, twelveHoursFormat, true)}-${this.formatTime(nextHour, twelveHoursFormat, true)}`;
|
|
945
698
|
}
|
|
946
699
|
/**
|
|
947
|
-
*
|
|
700
|
+
* Gets the minutely time range for the given date.
|
|
701
|
+
* @param date The date object.
|
|
702
|
+
* @param twelveHoursFormat Indicates whether to use twelve hours format.
|
|
703
|
+
* @returns The minutely time range string.
|
|
948
704
|
*/
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
if (selectedInterval === TIME_RANGE_INTERVAL_UNITS_VALUES.custom) {
|
|
954
|
-
this.updateDisabledAggregationOptions();
|
|
955
|
-
return;
|
|
956
|
-
}
|
|
957
|
-
this.handleNonCustomInterval(selectedInterval);
|
|
958
|
-
});
|
|
705
|
+
getMinutelyTimeRange(date, twelveHoursFormat) {
|
|
706
|
+
const nextMinute = new Date(date.getTime());
|
|
707
|
+
nextMinute.setMinutes(date.getMinutes() + 1);
|
|
708
|
+
return `${this.formatTime(date, twelveHoursFormat, false)}-${this.formatTime(nextMinute, twelveHoursFormat, false)}`;
|
|
959
709
|
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
710
|
+
/**
|
|
711
|
+
* Formats the given date into a time string.
|
|
712
|
+
* @param date The date to format.
|
|
713
|
+
* @param usePeriod Indicates whether to include the period (AM/PM) in the formatted time.
|
|
714
|
+
* @param useHourOnly Indicates whether to include only the hour part in the formatted time.
|
|
715
|
+
* @returns The formatted time string.
|
|
716
|
+
*/
|
|
717
|
+
formatTime(date, usePeriod, useHourOnly) {
|
|
718
|
+
const hours = date.getHours();
|
|
719
|
+
const minutes = date.getMinutes().toString().padStart(2, '0');
|
|
720
|
+
if (usePeriod) {
|
|
721
|
+
const period = hours >= 12 ? 'PM' : 'AM';
|
|
722
|
+
const formattedHours = hours % 12 === 0 ? 12 : hours % 12;
|
|
723
|
+
return `${formattedHours}:${useHourOnly ? '00' : minutes} ${period}`;
|
|
724
|
+
}
|
|
725
|
+
else {
|
|
726
|
+
return `${hours.toString().padStart(2, '0')}:${useHourOnly ? '00' : minutes}`;
|
|
727
|
+
}
|
|
965
728
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
729
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: AdjustAggregatedTimeRangePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
730
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.9", ngImport: i0, type: AdjustAggregatedTimeRangePipe, isStandalone: true, name: "adjustAggregatedTimeRange" }); }
|
|
731
|
+
}
|
|
732
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: AdjustAggregatedTimeRangePipe, decorators: [{
|
|
733
|
+
type: Pipe,
|
|
734
|
+
args: [{
|
|
735
|
+
name: 'adjustAggregatedTimeRange',
|
|
736
|
+
standalone: true
|
|
737
|
+
}]
|
|
738
|
+
}] });
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Applies CSS classes based on the value's range.
|
|
742
|
+
*/
|
|
743
|
+
class ApplyRangeClassPipe {
|
|
744
|
+
/**
|
|
745
|
+
* Transforms the input value based on the specified ranges.
|
|
746
|
+
*
|
|
747
|
+
* @param value - Initial value used to determine the CSS class.
|
|
748
|
+
* @param ranges - An object containing the min and max range values for yellow and red colors.
|
|
749
|
+
* @returns The CSS class to be applied.
|
|
750
|
+
*/
|
|
751
|
+
transform(value, ranges) {
|
|
752
|
+
if (value == null) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
if (value >= ranges.yellowRangeMin && value < ranges.yellowRangeMax) {
|
|
756
|
+
return 'text-warning';
|
|
757
|
+
}
|
|
758
|
+
else if (value >= ranges.redRangeMin && value <= ranges.redRangeMax) {
|
|
759
|
+
return 'text-danger';
|
|
760
|
+
}
|
|
761
|
+
return 'default';
|
|
762
|
+
}
|
|
763
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ApplyRangeClassPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
764
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.9", ngImport: i0, type: ApplyRangeClassPipe, isStandalone: true, name: "applyRangeClass" }); }
|
|
765
|
+
}
|
|
766
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ApplyRangeClassPipe, decorators: [{
|
|
767
|
+
type: Pipe,
|
|
768
|
+
args: [{
|
|
769
|
+
name: 'applyRangeClass',
|
|
770
|
+
standalone: true
|
|
771
|
+
}]
|
|
772
|
+
}] });
|
|
773
|
+
|
|
774
|
+
class DatapointsReloadComponent extends IntervalBasedReload {
|
|
775
|
+
constructor(cdRef, injector, translateService) {
|
|
776
|
+
super();
|
|
777
|
+
this.cdRef = cdRef;
|
|
778
|
+
this.injector = injector;
|
|
779
|
+
this.translateService = translateService;
|
|
780
|
+
/**
|
|
781
|
+
* @inheritdoc
|
|
782
|
+
*/
|
|
783
|
+
this.onCountdownEnded = new EventEmitter();
|
|
975
784
|
/**
|
|
976
|
-
*
|
|
977
|
-
* When form is saved it saves with a correct value, even without with this fix.
|
|
978
|
-
* How to reproduce:
|
|
979
|
-
* 1. set date values to e.g.: 01.05.2024-30.05.2024 and save it
|
|
980
|
-
* 2. reopen a config and switch interval to last month
|
|
785
|
+
* @inheritdoc
|
|
981
786
|
*/
|
|
982
|
-
|
|
983
|
-
// Form will be still saved with correct date value, only view is out of a sync.
|
|
984
|
-
this.formGroup.controls.dateFrom.setValue(dateRange.dateFrom);
|
|
985
|
-
this.isIntervalSelectorChangedProgrammatically = false;
|
|
986
|
-
// });
|
|
987
|
-
}
|
|
988
|
-
updateDisabledAggregationOptions() {
|
|
989
|
-
this.disabledAggregationOptions =
|
|
990
|
-
this.datapointsTableConfigService.updateDisabledStateOfAggregationOptionEntries(this.formGroup.controls.dateFrom.value, this.formGroup.controls.dateTo.value, AGGREGATION_VALUES_ARR);
|
|
991
|
-
}
|
|
992
|
-
updateAggregationIfAutoRefreshDisabled(selectedInterval) {
|
|
993
|
-
const isAutoRefreshDisabled = !this.formGroup.controls.isAutoRefreshEnabled.value;
|
|
994
|
-
if (isAutoRefreshDisabled) {
|
|
995
|
-
this.setAggregationValue(selectedInterval);
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
setAggregationValue(interval) {
|
|
999
|
-
const aggregationControl = this.formGroup.controls.aggregation;
|
|
1000
|
-
const newAggregationValue = this.datapointsTableConfigService.determineAggregationValue(interval);
|
|
1001
|
-
aggregationControl.setValue(newAggregationValue);
|
|
1002
|
-
}
|
|
1003
|
-
/**
|
|
1004
|
-
* Handles changes in the date selector form control.
|
|
1005
|
-
*/
|
|
1006
|
-
handleDateSelectorChanges() {
|
|
1007
|
-
merge(this.formGroup.controls.dateFrom.valueChanges, this.formGroup.controls.dateTo.valueChanges)
|
|
1008
|
-
.pipe(takeUntil(this.destroy$))
|
|
1009
|
-
.subscribe(() => this.handleDateChange());
|
|
787
|
+
this.manuallyDisabledCountdown = false;
|
|
1010
788
|
}
|
|
1011
|
-
|
|
1012
|
-
if (this.
|
|
789
|
+
ngOnChanges(changes) {
|
|
790
|
+
if (this.isDisabled && this.isAutoRefreshEnabled) {
|
|
791
|
+
this.isIntervalRefreshToggleOn = false;
|
|
792
|
+
this.disableCountdown();
|
|
1013
793
|
return;
|
|
1014
794
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
this.
|
|
795
|
+
if (this.isExportModalOpen && this.canDisableCountdownWhenModalIsOpen()) {
|
|
796
|
+
this.isIntervalRefreshToggleOn = !this.isIntervalRefreshToggleOn;
|
|
797
|
+
this.disableCountdown();
|
|
1018
798
|
}
|
|
1019
799
|
else {
|
|
1020
|
-
this.
|
|
800
|
+
if (this.canStartCountdown(changes)) {
|
|
801
|
+
this.isIntervalRefreshToggleOn = true;
|
|
802
|
+
this.startCountdown(this.injector);
|
|
803
|
+
}
|
|
804
|
+
if (this.canHandleScrolling()) {
|
|
805
|
+
this.handleScrolling();
|
|
806
|
+
}
|
|
1021
807
|
}
|
|
1022
|
-
this.setToFirstAvailableAggregationOptionIfCurrentAggregationIsDisabled();
|
|
1023
808
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
809
|
+
/**
|
|
810
|
+
* @inheritdoc
|
|
811
|
+
*/
|
|
812
|
+
countdownEnded() {
|
|
813
|
+
/**
|
|
814
|
+
* @inheritdoc
|
|
815
|
+
*/
|
|
816
|
+
this.autoRefreshList();
|
|
1026
817
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
818
|
+
reload() {
|
|
819
|
+
/**
|
|
820
|
+
* @inheritdoc
|
|
821
|
+
*/
|
|
822
|
+
this.autoRefreshList();
|
|
1029
823
|
}
|
|
1030
824
|
/**
|
|
1031
|
-
*
|
|
825
|
+
* @inheritdoc
|
|
1032
826
|
*/
|
|
1033
|
-
|
|
1034
|
-
this.
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
// Condition will be removed in MTM-61920
|
|
1039
|
-
// if (selected === DATE_SELECTION_VALUES.dashboard_context) {
|
|
1040
|
-
// this.handleDashboardContext();
|
|
1041
|
-
// } else {
|
|
1042
|
-
this.handleNonDashboardContext();
|
|
1043
|
-
// }
|
|
1044
|
-
});
|
|
1045
|
-
}
|
|
1046
|
-
handleDashboardContext() {
|
|
1047
|
-
this.isWidgetLinkedToGlobalTimeContext = true;
|
|
827
|
+
enableCountdown() {
|
|
828
|
+
this.hideCountdown = false;
|
|
829
|
+
// Prevents the countdown from getting stuck on an initial value.
|
|
830
|
+
this.cdRef.detectChanges();
|
|
831
|
+
this.startCountdown(this.injector);
|
|
1048
832
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
833
|
+
/**
|
|
834
|
+
* @inheritdoc
|
|
835
|
+
*/
|
|
836
|
+
updateCountdownButtonTooltipText() {
|
|
837
|
+
this.toggleCountdownButtonTooltipText = this.isIntervalRefreshToggleOn
|
|
838
|
+
? this.translateService.instant(gettext('Disable auto refresh'))
|
|
839
|
+
: this.translateService.instant(gettext('Enable auto refresh'));
|
|
1056
840
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
841
|
+
canStartCountdown(changes) {
|
|
842
|
+
const { isAutoRefreshEnabled, refreshInterval } = changes;
|
|
843
|
+
return ((isAutoRefreshEnabled || refreshInterval) &&
|
|
844
|
+
(this.isAutoRefreshEnabled || !!this.refreshInterval));
|
|
1059
845
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
const isDashboardTimeOptionSetAggregationToNull = !aggregationControl.value;
|
|
1063
|
-
if (isDashboardTimeOptionSetAggregationToNull) {
|
|
1064
|
-
aggregationControl.setValue(AGGREGATION_VALUES.none);
|
|
1065
|
-
}
|
|
846
|
+
canHandleScrolling() {
|
|
847
|
+
return !this.manuallyDisabledCountdown && this.isAutoRefreshEnabled;
|
|
1066
848
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
* - If the current option is disabled, sets the control to the first available (non-disabled) option based on the following order:
|
|
1074
|
-
* - If the current value is `DAILY`, it switches to `HOURLY` if it's not disabled, otherwise to `MINUTELY` if `HOURLY` is also disabled.
|
|
1075
|
-
* - If the current value is `HOURLY`, it switches to `MINUTELY` if it's not disabled.
|
|
1076
|
-
* - If all options are disabled, it sets the value to `NONE`.
|
|
1077
|
-
*
|
|
1078
|
-
* The disabled state is stored in the `disabledAggregationOptions` object,
|
|
1079
|
-
* where the key is the aggregation value and the value is a boolean indicating whether the option is disabled.
|
|
1080
|
-
*
|
|
1081
|
-
* The `AGGREGATION_VALUES` object defines the possible aggregation values.
|
|
1082
|
-
*/
|
|
1083
|
-
// TODO same method is in datapoints export component. Consider moving it to a shared service when DPT will be integrating with MTM-59689.
|
|
1084
|
-
// TODO 'determineNewAggregationValue' will be a shared method.
|
|
1085
|
-
// Integration task: MTM-60221
|
|
1086
|
-
setToFirstAvailableAggregationOptionIfCurrentAggregationIsDisabled() {
|
|
1087
|
-
const aggregationControl = this.formGroup.controls.aggregation;
|
|
1088
|
-
const currentValue = aggregationControl.value;
|
|
1089
|
-
const newAggregationValue = this.datapointsTableConfigService.determineNewAggregationValue(currentValue, this.disabledAggregationOptions);
|
|
1090
|
-
if (newAggregationValue !== currentValue) {
|
|
1091
|
-
aggregationControl.setValue(newAggregationValue);
|
|
1092
|
-
}
|
|
849
|
+
canDisableCountdownWhenModalIsOpen() {
|
|
850
|
+
return (this.isAutoRefreshEnabled &&
|
|
851
|
+
!!this.refreshInterval &&
|
|
852
|
+
!this.manuallyDisabledCountdown &&
|
|
853
|
+
!this.isScrolling &&
|
|
854
|
+
!this.hideCountdown);
|
|
1093
855
|
}
|
|
1094
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type:
|
|
1095
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DatapointsTableWidgetConfigComponent, isStandalone: true, selector: "c8y-datapoints-table-view-config", inputs: { config: "config" }, ngImport: i0, template: "<div class=\"p-l-24 p-r-24\">\n <form\n class=\"no-card-context\"\n [formGroup]=\"formGroup\"\n >\n <!-- <datapoints-selector> -->\n <div class=\"col-md-6\">\n <div class=\"row\">\n <!-- global-time-context-selector -->\n <div class=\"form-group m-b-0 p-b-16 separator-bottom\">\n <label\n class=\"d-flex a-i-center p-t-4\"\n for=\"dateSelection\"\n >\n {{ 'Date selection' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"popoverTemplate\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n </label>\n <ng-template #popoverTemplate>\n <span translate>\n Choose how to select a date range, the available options are:\n <ul class=\"m-l-0 p-l-8 m-t-8 m-b-0\">\n <!-- Will be re-enabled in MTM-61920 -->\n <!-- <li>\n <b>Dashboard time range:</b>\n restricts date selection to the global dashboard configuration only\n </li> -->\n <li>\n <b>Widget configuration:</b>\n restricts the date selection only to the widget configuration\n </li>\n <li>\n <b>Widget and widget configuration:</b>\n restricts the date selection to the widget view and widget configuration only\n </li>\n </ul>\n </span>\n </ng-template>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control text-12\"\n [title]=\"'Date selection' | translate\"\n [attr.aria-label]=\"'Date selection' | translate\"\n id=\"globalDateSelector\"\n formControlName=\"globalDateSelector\"\n >\n <option\n *ngFor=\"let dataSelectionValue of DATE_SELECTION_VALUES_ARR\"\n [ngValue]=\"dataSelectionValue\"\n >\n {{ DATE_SELECTION_LABELS[dataSelectionValue] | translate }}\n </option>\n </select>\n </div>\n </div>\n </div>\n <div class=\"row\">\n <c8y-datapoint-selection-list\n class=\"bg-inherit separator-top p-t-16 d-block\"\n listTitle=\"{{ 'Data points' | translate }}\"\n name=\"datapoints\"\n [defaultFormOptions]=\"defaultFormOptions\"\n [config]=\"datapointSelectionConfig\"\n [minActiveCount]=\"1\"\n formControlName=\"datapoints\"\n ></c8y-datapoint-selection-list>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"row\">\n <ng-container *ngIf=\"!isWidgetLinkedToGlobalTimeContext\">\n <div class=\"col-md-6\">\n <!-- interval selector -->\n <fieldset class=\"c8y-fieldset\">\n <legend class=\"d-flex a-i-center\">\n {{ 'Auto refresh' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"\n 'Change the state of interval automatic refresh and set the refresh frequency.'\n | translate\n \"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n </legend>\n <c8y-form-group class=\"m-b-16 form-group-sm\">\n <div class=\"d-flex gap-4 m-t-8 m-b-8 a-i-center\">\n <label class=\"c8y-switch\">\n <input\n id=\"refreshToggle\"\n name=\"isAutoRefreshEnabled\"\n type=\"checkbox\"\n formControlName=\"isAutoRefreshEnabled\"\n (click)=\"toggleRefreshIntervalControl()\"\n />\n <span></span>\n <span class=\"sr-only\">{{ 'Auto refresh' | translate }}</span>\n </label>\n <label\n class=\"m-b-0\"\n for=\"refreshInterval\"\n >\n {{ 'Interval' | translate }}\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control text-12\"\n [title]=\"'Refresh interval in seconds' | translate\"\n id=\"refreshInterval\"\n formControlName=\"refreshInterval\"\n >\n <option\n *ngFor=\"let refreshInterval of REFRESH_INTERVAL_VALUES_ARR\"\n [ngValue]=\"refreshInterval\"\n >\n {{ '{{ seconds }} s' | translate: { seconds: refreshInterval / 1000 } }}\n </option>\n </select>\n </div>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n </ng-container>\n <!-- decimal input -->\n <div class=\"col-md-6\">\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Decimal places' | translate }}\n </legend>\n <c8y-form-group class=\"p-t-8\">\n <input\n class=\"form-control\"\n name=\"decimalPlaces\"\n type=\"number\"\n formControlName=\"decimalPlaces\"\n step=\"1\"\n />\n </c8y-form-group>\n </fieldset>\n </div>\n </div>\n <!-- aggregation selector -->\n <ng-container *ngIf=\"!isWidgetLinkedToGlobalTimeContext\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Aggregation' | translate }}\n </legend>\n <c8y-form-group class=\"p-t-8\">\n <div class=\"c8y-select-wrapper\">\n <!-- Setting below [attr.disabled] ensures that the control is visually disabled and user interaction is prevented,\n while still allowing the value to be updated and saved correctly in the form.\n This solution covers the case where the user enables auto-refresh, in which case aggregation must be set to NONE. -->\n <select\n class=\"form-control text-12\"\n [title]=\"'Aggregation' | translate\"\n id=\"aggregation\"\n formControlName=\"aggregation\"\n [attr.disabled]=\"formGroup.value.isAutoRefreshEnabled ? true : null\"\n >\n <option\n *ngFor=\"let aggregationValue of AGGREGATION_VALUES_ARR\"\n [ngValue]=\"aggregationValue\"\n [disabled]=\"disabledAggregationOptions[aggregationValue]\"\n >\n {{ AGGREGATION_LABELS[aggregationValue] | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n <!-- time interval selector -->\n <div class=\"col-md-6\">\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Time interval' | translate }}\n </legend>\n <c8y-form-group class=\"p-t-8\">\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control text-12\"\n [title]=\"'Interval' | translate\"\n id=\"interval\"\n formControlName=\"interval\"\n >\n <option\n *ngFor=\"let intervalValue of INTERVAL_VALUES_ARR\"\n [ngValue]=\"intervalValue\"\n >\n {{ TIME_RANGE_INTERVAL_LABELS[intervalValue] | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n </div>\n <!-- date pickers -->\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Date range' | translate }}\n </legend>\n <c8y-date-range-picker></c8y-date-range-picker>\n </fieldset>\n </ng-container>\n </div>\n </form>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "pipe", type: i1$1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i1$1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i1$1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1$1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: DatapointSelectorModule }, { kind: "component", type: i7.DatapointSelectionListComponent, selector: "c8y-datapoint-selection-list", inputs: ["actions", "allowDragAndDrop", "config", "defaultFormOptions", "maxActiveCount", "minActiveCount", "resolveContext", "listTitle"], outputs: ["isValid", "change"] }, { kind: "component", type: DateRangePickerComponent, selector: "c8y-date-range-picker", inputs: ["isEmittingDateChange", "showLabel"], outputs: ["updatedDate"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i8.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "ngmodule", type: ReactiveFormsModule }] }); }
|
|
856
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsReloadComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.Injector }, { token: i1$2.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
857
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DatapointsReloadComponent, isStandalone: true, selector: "c8y-datapoints-reload", inputs: { isAutoRefreshEnabled: "isAutoRefreshEnabled", isDisabled: "isDisabled", isLoading: "isLoading", isScrolling: "isScrolling", isExportModalOpen: "isExportModalOpen", refreshInterval: "refreshInterval" }, outputs: { onCountdownEnded: "onCountdownEnded" }, viewQueries: [{ propertyName: "countdownIntervalComponent", first: true, predicate: CountdownIntervalComponent, descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"a-i-center input-group p-t-4 p-b-4 max-width-fit m-l-auto\">\n <label\n class=\"m-b-0 m-r-8 text-label-small text-truncate flex-no-shrink\"\n title=\" {{ 'Auto refresh' | translate }}\"\n *ngIf=\"refreshInterval && isAutoRefreshEnabled\"\n >\n {{ 'Auto refresh' | translate }}\n </label>\n <div class=\"input-group\">\n <label\n class=\"toggle-countdown\"\n [class.toggle-countdown-disabled]=\"isDisabled\"\n [attr.aria-label]=\"toggleCountdownButtonTooltipText\"\n [tooltip]=\"toggleCountdownButtonTooltipText\"\n placement=\"bottom\"\n *ngIf=\"refreshInterval && isAutoRefreshEnabled\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"isIntervalRefreshToggleOn\"\n [disabled]=\"isDisabled\"\n data-cy=\"c8y-data-points-table-widget--interval-toggle-button\"\n (click)=\"onToggleCountdownButtonState($event)\"\n />\n\n <c8y-countdown-interval\n *ngIf=\"isIntervalRefreshToggleOn\"\n [countdownInterval]=\"refreshInterval\"\n (countdownEnded)=\"countdownEnded()\"\n ></c8y-countdown-interval>\n <i\n c8yIcon=\"pause\"\n *ngIf=\"!isIntervalRefreshToggleOn\"\n ></i>\n </label>\n\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n [attr.aria-label]=\"'Refresh' | translate\"\n [tooltip]=\"'Refresh' | translate\"\n placement=\"bottom\"\n type=\"button\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n [disabled]=\"isLoading()\"\n (click)=\"reload()\"\n data-cy=\"c8y-data-points-table-widget--reload-button\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': isLoading() }\"\n ></i>\n </button>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule$1 }, { kind: "directive", type: i1$1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: i1$1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: CountdownIntervalModule }, { kind: "component", type: i1$1.CountdownIntervalComponent, selector: "c8y-countdown-interval", inputs: ["countdownInterval"], outputs: ["countdownEnded"] }, { kind: "ngmodule", type: ListGroupModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i4$1.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }] }); }
|
|
1096
858
|
}
|
|
1097
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type:
|
|
859
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsReloadComponent, decorators: [{
|
|
1098
860
|
type: Component,
|
|
1099
|
-
args: [{ selector: 'c8y-datapoints-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
861
|
+
args: [{ selector: 'c8y-datapoints-reload', standalone: true, imports: [CommonModule$1, CountdownIntervalModule, ListGroupModule, TooltipModule], template: "<div class=\"a-i-center input-group p-t-4 p-b-4 max-width-fit m-l-auto\">\n <label\n class=\"m-b-0 m-r-8 text-label-small text-truncate flex-no-shrink\"\n title=\" {{ 'Auto refresh' | translate }}\"\n *ngIf=\"refreshInterval && isAutoRefreshEnabled\"\n >\n {{ 'Auto refresh' | translate }}\n </label>\n <div class=\"input-group\">\n <label\n class=\"toggle-countdown\"\n [class.toggle-countdown-disabled]=\"isDisabled\"\n [attr.aria-label]=\"toggleCountdownButtonTooltipText\"\n [tooltip]=\"toggleCountdownButtonTooltipText\"\n placement=\"bottom\"\n *ngIf=\"refreshInterval && isAutoRefreshEnabled\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"isIntervalRefreshToggleOn\"\n [disabled]=\"isDisabled\"\n data-cy=\"c8y-data-points-table-widget--interval-toggle-button\"\n (click)=\"onToggleCountdownButtonState($event)\"\n />\n\n <c8y-countdown-interval\n *ngIf=\"isIntervalRefreshToggleOn\"\n [countdownInterval]=\"refreshInterval\"\n (countdownEnded)=\"countdownEnded()\"\n ></c8y-countdown-interval>\n <i\n c8yIcon=\"pause\"\n *ngIf=\"!isIntervalRefreshToggleOn\"\n ></i>\n </label>\n\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n [attr.aria-label]=\"'Refresh' | translate\"\n [tooltip]=\"'Refresh' | translate\"\n placement=\"bottom\"\n type=\"button\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n [disabled]=\"isLoading()\"\n (click)=\"reload()\"\n data-cy=\"c8y-data-points-table-widget--reload-button\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': isLoading() }\"\n ></i>\n </button>\n </div>\n </div>\n</div>\n" }]
|
|
862
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.Injector }, { type: i1$2.TranslateService }], propDecorators: { countdownIntervalComponent: [{
|
|
863
|
+
type: ViewChild,
|
|
864
|
+
args: [CountdownIntervalComponent, { static: false }]
|
|
865
|
+
}], isAutoRefreshEnabled: [{
|
|
866
|
+
type: Input
|
|
867
|
+
}], isDisabled: [{
|
|
868
|
+
type: Input
|
|
869
|
+
}], isLoading: [{
|
|
870
|
+
type: Input
|
|
871
|
+
}], isScrolling: [{
|
|
872
|
+
type: Input
|
|
873
|
+
}], isExportModalOpen: [{
|
|
874
|
+
type: Input
|
|
875
|
+
}], refreshInterval: [{
|
|
1107
876
|
type: Input
|
|
877
|
+
}], onCountdownEnded: [{
|
|
878
|
+
type: Output
|
|
1108
879
|
}] } });
|
|
1109
880
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
* The output will be '9:00-10:00'.
|
|
1117
|
-
*/
|
|
1118
|
-
class AdjustAggregatedTimeRangePipe {
|
|
1119
|
-
/**
|
|
1120
|
-
* Transforms the input time based on the aggregation type.
|
|
1121
|
-
* @param inputTime The input time string.
|
|
1122
|
-
* @param aggregationType The type of aggregation (optional).
|
|
1123
|
-
* @returns The transformed time string.
|
|
1124
|
-
*/
|
|
1125
|
-
transform(inputTime, aggregationType$1) {
|
|
1126
|
-
if (!aggregationType$1) {
|
|
1127
|
-
return inputTime;
|
|
1128
|
-
}
|
|
1129
|
-
if (aggregationType$1 === aggregationType.DAILY) {
|
|
1130
|
-
return '';
|
|
1131
|
-
}
|
|
1132
|
-
const date = this.createDateFromInput(inputTime);
|
|
1133
|
-
const isTwelveHoursFormat = this.isTwelveHoursFormat(inputTime);
|
|
1134
|
-
switch (aggregationType$1) {
|
|
1135
|
-
case aggregationType.HOURLY:
|
|
1136
|
-
return this.getHourlyTimeRange(date, isTwelveHoursFormat);
|
|
1137
|
-
case aggregationType.MINUTELY:
|
|
1138
|
-
return this.getMinutelyTimeRange(date, isTwelveHoursFormat);
|
|
1139
|
-
default:
|
|
1140
|
-
throw new Error('Unsupported aggregation type');
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
/**
|
|
1144
|
-
* Creates a date object from the input time string.
|
|
1145
|
-
* @param inputTime The input time string.
|
|
1146
|
-
* @returns The created Date object.
|
|
1147
|
-
*/
|
|
1148
|
-
createDateFromInput(inputTime) {
|
|
1149
|
-
const defaultDate = '1970-01-01 ';
|
|
1150
|
-
const isPM = /PM/i.test(inputTime);
|
|
1151
|
-
const cleanedTime = inputTime.replace(/AM|PM/i, '').trim();
|
|
1152
|
-
this.validateTimeFormat(cleanedTime, inputTime);
|
|
1153
|
-
const dateTimeString = `${defaultDate}${cleanedTime}`;
|
|
1154
|
-
const date = new Date(dateTimeString);
|
|
1155
|
-
if (isNaN(date.getTime())) {
|
|
1156
|
-
throw new Error('Invalid input time');
|
|
1157
|
-
}
|
|
1158
|
-
return this.adjustForPMTime(date, isPM);
|
|
881
|
+
function mapToSourceValueObject([key, value]) {
|
|
882
|
+
return { key, value };
|
|
883
|
+
}
|
|
884
|
+
class DatapointsTableViewService {
|
|
885
|
+
constructor(dataFetchingService) {
|
|
886
|
+
this.dataFetchingService = dataFetchingService;
|
|
1159
887
|
}
|
|
1160
888
|
/**
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1163
|
-
* @param
|
|
1164
|
-
* @
|
|
889
|
+
* Filters out inactive data points from the given array.
|
|
890
|
+
*
|
|
891
|
+
* @param datapoints - The array of data points to filter.
|
|
892
|
+
* @returns An array of data points that are active.
|
|
1165
893
|
*/
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
this.validateTimeParts(parts);
|
|
1169
|
-
const [hoursStr, minutesStr, secondsStr] = parts;
|
|
1170
|
-
this.validateTimeDigits(hoursStr, minutesStr, secondsStr);
|
|
1171
|
-
const { hours, minutes, seconds } = this.parseTimeComponents(hoursStr, minutesStr, secondsStr);
|
|
1172
|
-
this.validateTimeRanges(hours, minutes, seconds);
|
|
1173
|
-
this.validateTimeFormat24Hour(hours, originalInput);
|
|
1174
|
-
}
|
|
1175
|
-
validateTimeParts(parts) {
|
|
1176
|
-
if (parts.length < 2 || parts.length > 3) {
|
|
1177
|
-
throw new Error('Invalid input time');
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
validateTimeDigits(hoursStr, minutesStr, secondsStr) {
|
|
1181
|
-
if (!this.isValidNumberString(hoursStr) ||
|
|
1182
|
-
!this.isValidNumberString(minutesStr) ||
|
|
1183
|
-
(secondsStr !== undefined && !this.isValidNumberString(secondsStr))) {
|
|
1184
|
-
throw new Error('Invalid input time');
|
|
1185
|
-
}
|
|
894
|
+
filterOutInactiveDatapoints(datapoints) {
|
|
895
|
+
return datapoints.filter((datapoint) => datapoint.__active);
|
|
1186
896
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
minutes: Number(minutesStr),
|
|
1191
|
-
seconds: secondsStr ? Number(secondsStr) : 0
|
|
1192
|
-
};
|
|
897
|
+
hasMultipleDatapoints(datapoints) {
|
|
898
|
+
const ids = datapoints.map(dp => dp.__target.id);
|
|
899
|
+
return ids.length > 1;
|
|
1193
900
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
901
|
+
/**
|
|
902
|
+
* Returns a map of active data points device IDs with their corresponding series.
|
|
903
|
+
*
|
|
904
|
+
* Example output:
|
|
905
|
+
* ```typescript
|
|
906
|
+
* new Map([
|
|
907
|
+
* [
|
|
908
|
+
* "844657202",
|
|
909
|
+
* [
|
|
910
|
+
* "c8y_Temperature.T"
|
|
911
|
+
* ]
|
|
912
|
+
* ],
|
|
913
|
+
* [
|
|
914
|
+
* "32666427",
|
|
915
|
+
* [
|
|
916
|
+
* "c8y_Battery.Battery"
|
|
917
|
+
* ]
|
|
918
|
+
* ]
|
|
919
|
+
* ]);
|
|
920
|
+
* ```
|
|
921
|
+
* @param datapoints - An array of data points.
|
|
922
|
+
* @returns A map where the key is the data point ID and the value is an array of data point series.
|
|
923
|
+
*/
|
|
924
|
+
groupSeriesByDeviceId(activeDatapoints) {
|
|
925
|
+
return activeDatapoints.reduce((map, { fragment, series, __target: { id } }) => {
|
|
926
|
+
const value = `${fragment}.${series}`;
|
|
927
|
+
const existingValue = map.get(id) ?? [];
|
|
928
|
+
map.set(id, [...existingValue, value]);
|
|
929
|
+
return map;
|
|
930
|
+
}, new Map());
|
|
1198
931
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
932
|
+
/**
|
|
933
|
+
* Retrieves the active data points series data and returns it as a map.
|
|
934
|
+
*
|
|
935
|
+
* @param datapointsValuesDataMap - A map of data point sources with their associated series.
|
|
936
|
+
* @param config - The configuration of the data points table.
|
|
937
|
+
* @param roundSeconds - Whether to round the seconds or not.
|
|
938
|
+
* If true, the seconds will be rounded to 0.
|
|
939
|
+
* If false, the seconds will be displayed as they are.
|
|
940
|
+
* @returns A Promise that resolves to a Map object with data point IDs as keys and DataObject as values or undefined when all series has forbidden access.
|
|
941
|
+
*/
|
|
942
|
+
async getAllActiveSeriesDataMap(datapointsValuesDataMap, config, roundSeconds) {
|
|
943
|
+
const promises = Array.from(datapointsValuesDataMap).map(async ([source, series]) => {
|
|
944
|
+
const params = {
|
|
945
|
+
dateFrom: config.dateFrom,
|
|
946
|
+
dateTo: config.dateTo,
|
|
947
|
+
source,
|
|
948
|
+
series,
|
|
949
|
+
aggregationType: config.aggregation
|
|
950
|
+
};
|
|
951
|
+
const { data, res } = await this.dataFetchingService.fetchSeriesData(params, roundSeconds);
|
|
952
|
+
return { source, data, res };
|
|
953
|
+
});
|
|
954
|
+
const results = await Promise.all(promises);
|
|
955
|
+
const allSeriesHasForbiddenAccess = results.every(item => item.res?.status === 403);
|
|
956
|
+
if (allSeriesHasForbiddenAccess) {
|
|
957
|
+
throw new Error('Access forbidden: All series have a 403 status code');
|
|
1202
958
|
}
|
|
959
|
+
const filteredResults = this.filterOutElementsWithForbiddenResponses(results);
|
|
960
|
+
const resultMap = new Map();
|
|
961
|
+
filteredResults.forEach(result => resultMap.set(result.source, result.data));
|
|
962
|
+
return resultMap;
|
|
1203
963
|
}
|
|
1204
964
|
/**
|
|
1205
|
-
*
|
|
1206
|
-
*
|
|
1207
|
-
*
|
|
965
|
+
* Creates an array of DatapointsWithValues based on the provided datapoints and datapointsSeriesDataMap.
|
|
966
|
+
*
|
|
967
|
+
* Finds an index of a current data point within series object and based on that index filters values array.
|
|
968
|
+
*
|
|
969
|
+
* @param datapoints - An array of data points.
|
|
970
|
+
* @param datapointsSeriesDataMap - A map containing series data for data points.
|
|
971
|
+
* @returns An array of DatapointsWithValues.
|
|
1208
972
|
*/
|
|
1209
|
-
|
|
1210
|
-
return (
|
|
1211
|
-
|
|
1212
|
-
|
|
973
|
+
getDatapointsWithValues(datapoints, datapointsSeriesDataMap) {
|
|
974
|
+
return datapoints.map((dp) => {
|
|
975
|
+
const seriesData = datapointsSeriesDataMap.get(dp.__target.id);
|
|
976
|
+
if (!seriesData) {
|
|
977
|
+
return { ...dp, values: {} };
|
|
978
|
+
}
|
|
979
|
+
// Find an index of a corresponding datapoint data, within series object.
|
|
980
|
+
const datapointSeriesArrayIndex = seriesData.series.findIndex(s => s.name === dp.series && s.type === dp.fragment);
|
|
981
|
+
const valuesFilteredByDatapointSeriesArrayIndex = Object.fromEntries(Object.entries(seriesData.values).map(([key, arr]) => [
|
|
982
|
+
key,
|
|
983
|
+
arr.filter((_, index) => index === datapointSeriesArrayIndex)
|
|
984
|
+
]));
|
|
985
|
+
return {
|
|
986
|
+
...dp,
|
|
987
|
+
seriesUnit: seriesData.series[datapointSeriesArrayIndex]?.unit,
|
|
988
|
+
values: valuesFilteredByDatapointSeriesArrayIndex
|
|
989
|
+
};
|
|
990
|
+
});
|
|
1213
991
|
}
|
|
1214
992
|
/**
|
|
1215
|
-
*
|
|
1216
|
-
*
|
|
1217
|
-
* @
|
|
993
|
+
* Creates the column headers for the devices in the data points table.
|
|
994
|
+
*
|
|
995
|
+
* @param datapointsWithValues - An array of data points.
|
|
996
|
+
* @returns An array of column headers for the devices.
|
|
1218
997
|
*/
|
|
1219
|
-
|
|
1220
|
-
return
|
|
998
|
+
getColumnHeaders(datapointsWithValues) {
|
|
999
|
+
return datapointsWithValues.map(({ __target: { name }, label, renderType, unit }) => {
|
|
1000
|
+
return { deviceName: name, label, renderType, unit };
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
mapDatapointsWithValuesToList(datapointsWithValues) {
|
|
1004
|
+
return datapointsWithValues.flatMap(dp => {
|
|
1005
|
+
if (!dp.values) {
|
|
1006
|
+
return [];
|
|
1007
|
+
}
|
|
1008
|
+
return Object.entries(dp.values).flatMap(([date, valuesArray]) => {
|
|
1009
|
+
const value = this.findMinMaxValues(valuesArray);
|
|
1010
|
+
if (value == null) {
|
|
1011
|
+
return [];
|
|
1012
|
+
}
|
|
1013
|
+
return [
|
|
1014
|
+
{
|
|
1015
|
+
dateAndTime: date,
|
|
1016
|
+
deviceName: dp.__target.name,
|
|
1017
|
+
fragment: dp.fragment,
|
|
1018
|
+
label: dp.label,
|
|
1019
|
+
redRangeMax: dp.redRangeMax,
|
|
1020
|
+
redRangeMin: dp.redRangeMin,
|
|
1021
|
+
renderType: dp.renderType,
|
|
1022
|
+
series: dp.series,
|
|
1023
|
+
value: value,
|
|
1024
|
+
yellowRangeMax: dp.yellowRangeMax,
|
|
1025
|
+
yellowRangeMin: dp.yellowRangeMin
|
|
1026
|
+
}
|
|
1027
|
+
];
|
|
1028
|
+
});
|
|
1029
|
+
});
|
|
1221
1030
|
}
|
|
1222
1031
|
/**
|
|
1223
|
-
*
|
|
1224
|
-
*
|
|
1225
|
-
*
|
|
1226
|
-
*
|
|
1032
|
+
* Finds the overall minimum and maximum values from an array of objects containing 'min' and 'max' properties.
|
|
1033
|
+
*
|
|
1034
|
+
* If the array contains only one object, that object's 'min' and 'max' values will be returned.
|
|
1035
|
+
*
|
|
1036
|
+
* @param valuesArray - An array with objects, where each contains 'min' and 'max' properties.
|
|
1037
|
+
* @returns An object with the smallest 'min' and largest 'max' values found in the array.
|
|
1038
|
+
*
|
|
1039
|
+
* @example
|
|
1040
|
+
* const values = [
|
|
1041
|
+
* { min: 1, max: 10 }
|
|
1042
|
+
* ];
|
|
1043
|
+
*
|
|
1044
|
+
* const result = findMinMaxValues(values);
|
|
1045
|
+
* // result is { min: 1, max: 10 }
|
|
1227
1046
|
*/
|
|
1228
|
-
|
|
1229
|
-
const
|
|
1230
|
-
if (
|
|
1231
|
-
|
|
1232
|
-
}
|
|
1233
|
-
else if (!isPM && hours === 12) {
|
|
1234
|
-
date.setHours(0);
|
|
1047
|
+
findMinMaxValues(valuesArray) {
|
|
1048
|
+
const validItems = valuesArray.filter((item) => item && item.min != null && item.max != null);
|
|
1049
|
+
if (validItems.length === 0) {
|
|
1050
|
+
return null;
|
|
1235
1051
|
}
|
|
1236
|
-
|
|
1052
|
+
const initialValue = { min: validItems[0].min, max: validItems[0].max };
|
|
1053
|
+
return validItems.reduce((acc, item) => ({
|
|
1054
|
+
min: Math.min(acc.min, item.min),
|
|
1055
|
+
max: Math.max(acc.max, item.max)
|
|
1056
|
+
}), initialValue);
|
|
1237
1057
|
}
|
|
1238
1058
|
/**
|
|
1239
|
-
*
|
|
1240
|
-
*
|
|
1241
|
-
* @
|
|
1059
|
+
* Groups a list of data points by date and device, based on given references.
|
|
1060
|
+
*
|
|
1061
|
+
* @param dataList - The list of data points to be grouped.
|
|
1062
|
+
* @param references - The column headers that serve as references for grouping.
|
|
1063
|
+
* @returns An array of grouped data points, where each group corresponds to a unique date and device.
|
|
1242
1064
|
*/
|
|
1243
|
-
|
|
1244
|
-
|
|
1065
|
+
groupByDateAndDevice(dataList, references) {
|
|
1066
|
+
const map = this.generateDataPointMap(dataList, references);
|
|
1067
|
+
return this.mergeDatapoints(map);
|
|
1245
1068
|
}
|
|
1246
1069
|
/**
|
|
1247
|
-
*
|
|
1248
|
-
*
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1070
|
+
* Generates and populates a map with data points.
|
|
1071
|
+
*
|
|
1072
|
+
* This function processes the provided data points and organizes them into a map structure
|
|
1073
|
+
* where each key is a unique combination of date and device identifiers, and the value is an
|
|
1074
|
+
* array of data points (or null values) associated with that key. This structured data is then
|
|
1075
|
+
* used in the data point table to render the data appropriately.
|
|
1076
|
+
*
|
|
1077
|
+
* @param dataList - The list of data point table items to be processed.
|
|
1078
|
+
* @param columnsHeadersReferences - The list of column headers used to determine the order and structure of the map values.
|
|
1079
|
+
* @returns A map where the key is a datapoint identifier containing the date and device name, and the value is an array of data point table items or null.
|
|
1251
1080
|
*/
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1081
|
+
generateDataPointMap(dataList, columnsHeadersReferences) {
|
|
1082
|
+
// Map to store the data points indexed by a unique identifier.
|
|
1083
|
+
const map = new Map();
|
|
1084
|
+
dataList.forEach(obj => {
|
|
1085
|
+
// Generate a unique identifier for each data point using the date and device name.
|
|
1086
|
+
const dateKey = this.toISOFormat(obj.dateAndTime);
|
|
1087
|
+
const datapointIdentifier = `${dateKey}_${obj.deviceName}`;
|
|
1088
|
+
// Initialize the map entry if it does not exist with a unique identifier.
|
|
1089
|
+
if (!map.has(datapointIdentifier)) {
|
|
1090
|
+
map.set(datapointIdentifier, Array(columnsHeadersReferences.length).fill(null));
|
|
1091
|
+
}
|
|
1092
|
+
// Find the index of the reference that matches the current data point.
|
|
1093
|
+
const matchingColumnIndex = columnsHeadersReferences.findIndex(ref => ref.deviceName === obj.deviceName && ref.label === obj.label);
|
|
1094
|
+
// Update the map entry with the data point.
|
|
1095
|
+
const tableItem = map.get(datapointIdentifier);
|
|
1096
|
+
if (tableItem) {
|
|
1097
|
+
tableItem[matchingColumnIndex] = { ...obj };
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
return map;
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Merges the data points from the given map into an array of grouped data point table items.
|
|
1104
|
+
*
|
|
1105
|
+
* @param map - The map containing the data points to be merged.
|
|
1106
|
+
* @returns An array of grouped data point table items.
|
|
1107
|
+
*/
|
|
1108
|
+
mergeDatapoints(map) {
|
|
1109
|
+
const mergedData = [];
|
|
1110
|
+
map.forEach((values, datapointIdentifier) => {
|
|
1111
|
+
const [dateKey, deviceName] = datapointIdentifier.split('_');
|
|
1112
|
+
const validDataPoint = values.some(item => item && Object.keys(item.value).length > 0);
|
|
1113
|
+
if (validDataPoint) {
|
|
1114
|
+
mergedData.push({
|
|
1115
|
+
dateAndTime: dateKey,
|
|
1116
|
+
deviceName: deviceName,
|
|
1117
|
+
rowItems: values.map(item => (item ? { ...item } : null))
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
1121
|
+
return mergedData;
|
|
1256
1122
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
* @param date The date object.
|
|
1260
|
-
* @param twelveHoursFormat Indicates whether to use twelve hours format.
|
|
1261
|
-
* @returns The minutely time range string.
|
|
1262
|
-
*/
|
|
1263
|
-
getMinutelyTimeRange(date, twelveHoursFormat) {
|
|
1264
|
-
const nextMinute = new Date(date.getTime());
|
|
1265
|
-
nextMinute.setMinutes(date.getMinutes() + 1);
|
|
1266
|
-
return `${this.formatTime(date, twelveHoursFormat, false)}-${this.formatTime(nextMinute, twelveHoursFormat, false)}`;
|
|
1123
|
+
sortDataByDateDescending(data) {
|
|
1124
|
+
return data.sort((a, b) => new Date(b.dateAndTime).getTime() - new Date(a.dateAndTime).getTime());
|
|
1267
1125
|
}
|
|
1268
1126
|
/**
|
|
1269
|
-
*
|
|
1270
|
-
*
|
|
1271
|
-
*
|
|
1272
|
-
*
|
|
1273
|
-
* @
|
|
1127
|
+
* Prepares the updated time range based on the selected interval.
|
|
1128
|
+
*
|
|
1129
|
+
* In case of a 'custom' interval or no quantity, the original date range is returned.
|
|
1130
|
+
*
|
|
1131
|
+
* @param interval - The selected interval type.
|
|
1132
|
+
* @returns An object containing the `dateFrom` and `dateTo` in ISO string format.
|
|
1274
1133
|
*/
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
if (usePeriod) {
|
|
1279
|
-
const period = hours >= 12 ? 'PM' : 'AM';
|
|
1280
|
-
const formattedHours = hours % 12 === 0 ? 12 : hours % 12;
|
|
1281
|
-
return `${formattedHours}:${useHourOnly ? '00' : minutes} ${period}`;
|
|
1134
|
+
prepareTimeRange(interval, dateFromInput, dateToInput) {
|
|
1135
|
+
if (!interval || interval === INTERVAL_VALUES.custom) {
|
|
1136
|
+
return { dateFrom: dateFromInput, dateTo: dateToInput };
|
|
1282
1137
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1138
|
+
const selected = DURATION_OPTIONS.find(selectedDuration => selectedDuration.id === interval);
|
|
1139
|
+
if (!selected?.amount) {
|
|
1140
|
+
return { dateFrom: dateFromInput, dateTo: dateToInput };
|
|
1285
1141
|
}
|
|
1142
|
+
const now = new Date();
|
|
1143
|
+
return {
|
|
1144
|
+
dateFrom: this.subtractTime(now, selected.amount, selected.unit).toISOString(),
|
|
1145
|
+
dateTo: now.toISOString()
|
|
1146
|
+
};
|
|
1286
1147
|
}
|
|
1287
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: AdjustAggregatedTimeRangePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1288
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.9", ngImport: i0, type: AdjustAggregatedTimeRangePipe, isStandalone: true, name: "adjustAggregatedTimeRange" }); }
|
|
1289
|
-
}
|
|
1290
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: AdjustAggregatedTimeRangePipe, decorators: [{
|
|
1291
|
-
type: Pipe,
|
|
1292
|
-
args: [{
|
|
1293
|
-
name: 'adjustAggregatedTimeRange',
|
|
1294
|
-
standalone: true
|
|
1295
|
-
}]
|
|
1296
|
-
}] });
|
|
1297
|
-
|
|
1298
|
-
/**
|
|
1299
|
-
* Applies CSS classes based on the value's range.
|
|
1300
|
-
*/
|
|
1301
|
-
class ApplyRangeClassPipe {
|
|
1302
1148
|
/**
|
|
1303
|
-
*
|
|
1149
|
+
* Subtracts an amount of time from a given date.
|
|
1304
1150
|
*
|
|
1305
|
-
* @param
|
|
1306
|
-
* @param
|
|
1307
|
-
* @
|
|
1151
|
+
* @param date - The original date.
|
|
1152
|
+
* @param amount - The amount of time units to subtract.
|
|
1153
|
+
* @param unit - The unit of time to subtract (e.g., minutes, hours, days, weeks, months).
|
|
1154
|
+
* @returns A new date with the specified time subtracted.
|
|
1308
1155
|
*/
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1156
|
+
subtractTime(date, amount, unit) {
|
|
1157
|
+
const newDate = new Date(date);
|
|
1158
|
+
switch (unit) {
|
|
1159
|
+
case INTERVAL_VALUES.minutes:
|
|
1160
|
+
newDate.setUTCMinutes(newDate.getUTCMinutes() - amount);
|
|
1161
|
+
break;
|
|
1162
|
+
case INTERVAL_VALUES.hours:
|
|
1163
|
+
newDate.setUTCHours(newDate.getUTCHours() - amount);
|
|
1164
|
+
break;
|
|
1165
|
+
case INTERVAL_VALUES.days:
|
|
1166
|
+
newDate.setUTCDate(newDate.getUTCDate() - amount);
|
|
1167
|
+
break;
|
|
1168
|
+
case INTERVAL_VALUES.weeks:
|
|
1169
|
+
newDate.setUTCDate(newDate.getUTCDate() - amount * 7);
|
|
1170
|
+
break;
|
|
1171
|
+
case INTERVAL_VALUES.months:
|
|
1172
|
+
this.subtractMonthsAndAdjustDay(newDate, amount);
|
|
1173
|
+
break;
|
|
1318
1174
|
}
|
|
1319
|
-
return
|
|
1320
|
-
}
|
|
1321
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ApplyRangeClassPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1322
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.9", ngImport: i0, type: ApplyRangeClassPipe, isStandalone: true, name: "applyRangeClass" }); }
|
|
1323
|
-
}
|
|
1324
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ApplyRangeClassPipe, decorators: [{
|
|
1325
|
-
type: Pipe,
|
|
1326
|
-
args: [{
|
|
1327
|
-
name: 'applyRangeClass',
|
|
1328
|
-
standalone: true
|
|
1329
|
-
}]
|
|
1330
|
-
}] });
|
|
1331
|
-
|
|
1332
|
-
class DatapointsReloadComponent extends IntervalBasedReload {
|
|
1333
|
-
constructor(cdRef, injector, translateService) {
|
|
1334
|
-
super();
|
|
1335
|
-
this.cdRef = cdRef;
|
|
1336
|
-
this.injector = injector;
|
|
1337
|
-
this.translateService = translateService;
|
|
1338
|
-
/**
|
|
1339
|
-
* @inheritdoc
|
|
1340
|
-
*/
|
|
1341
|
-
this.onCountdownEnded = new EventEmitter();
|
|
1342
|
-
/**
|
|
1343
|
-
* @inheritdoc
|
|
1344
|
-
*/
|
|
1345
|
-
this.manuallyDisabledCountdown = false;
|
|
1175
|
+
return newDate;
|
|
1346
1176
|
}
|
|
1347
|
-
|
|
1348
|
-
if (
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
return;
|
|
1177
|
+
getSeriesWithoutPermissionToRead(activeDatapointsSeriesData, activeDatapointsIdsWithSeries) {
|
|
1178
|
+
if (!activeDatapointsSeriesData) {
|
|
1179
|
+
// Returns all activeDatapointsIdsWithSeries entries if activeDatapointsSeriesData is undefined.
|
|
1180
|
+
// It means that the user does not have permission to see any of the selected datapoints data.
|
|
1181
|
+
return Array.from(activeDatapointsIdsWithSeries, mapToSourceValueObject);
|
|
1352
1182
|
}
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1183
|
+
const availableSources = new Set(activeDatapointsSeriesData.keys());
|
|
1184
|
+
return Array.from(activeDatapointsIdsWithSeries)
|
|
1185
|
+
.filter(([source]) => !availableSources.has(source))
|
|
1186
|
+
.map(mapToSourceValueObject);
|
|
1187
|
+
}
|
|
1188
|
+
hasSecondsAndMillisecondsEqualZero(timeString) {
|
|
1189
|
+
if (!timeString) {
|
|
1190
|
+
return false;
|
|
1356
1191
|
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
this.startCountdown(this.injector);
|
|
1361
|
-
}
|
|
1362
|
-
if (this.canHandleScrolling()) {
|
|
1363
|
-
this.handleScrolling();
|
|
1364
|
-
}
|
|
1192
|
+
const date = new Date(timeString);
|
|
1193
|
+
if (isNaN(date.getTime())) {
|
|
1194
|
+
return false;
|
|
1365
1195
|
}
|
|
1196
|
+
return date.getUTCSeconds() === 0 && date.getUTCMilliseconds() === 0;
|
|
1366
1197
|
}
|
|
1367
1198
|
/**
|
|
1368
|
-
*
|
|
1199
|
+
* Converts a date string to ISO format.
|
|
1200
|
+
*
|
|
1201
|
+
* @param dateStr - The date string to convert.
|
|
1202
|
+
* @returns The ISO format of the given date string.
|
|
1369
1203
|
*/
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
* @inheritdoc
|
|
1373
|
-
*/
|
|
1374
|
-
this.autoRefreshList();
|
|
1204
|
+
toISOFormat(dateStr) {
|
|
1205
|
+
return new Date(dateStr).toISOString();
|
|
1375
1206
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1207
|
+
filterOutElementsWithForbiddenResponses(seriesDataWithResponse) {
|
|
1208
|
+
return seriesDataWithResponse.filter(item => {
|
|
1209
|
+
return !(item.res?.status === 403);
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
subtractMonthsAndAdjustDay(date, monthsToSubtract) {
|
|
1213
|
+
const currentMonth = date.getUTCMonth();
|
|
1214
|
+
const expectedTargetMonth = this.calculateTargetMonth(currentMonth, monthsToSubtract);
|
|
1215
|
+
date.setUTCMonth(currentMonth - monthsToSubtract);
|
|
1216
|
+
const actualMonth = date.getUTCMonth();
|
|
1217
|
+
const dayDoesNotExistInTargetMonth = actualMonth !== expectedTargetMonth;
|
|
1218
|
+
if (dayDoesNotExistInTargetMonth) {
|
|
1219
|
+
this.setToLastDayOfPreviousMonth(date);
|
|
1220
|
+
}
|
|
1381
1221
|
}
|
|
1382
1222
|
/**
|
|
1383
|
-
*
|
|
1223
|
+
* Calculates the target month number (0-11) after subtracting months from the current month.
|
|
1224
|
+
* Handles negative month numbers by normalizing them to the valid 0-11 range.
|
|
1225
|
+
*
|
|
1226
|
+
* Examples:
|
|
1227
|
+
* - January(0) - 1 month = December(11)
|
|
1228
|
+
* - March(2) - 4 months = November(10)
|
|
1229
|
+
* - December(11) - 1 month = November(10)
|
|
1230
|
+
*
|
|
1231
|
+
* @param currentMonth - Current month (0-11, where 0 is January)
|
|
1232
|
+
* @param monthsToSubtract - Number of months to subtract
|
|
1233
|
+
* @returns Normalized month number in range 0-11
|
|
1384
1234
|
*/
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
// Prevents the countdown from getting stuck on an initial value.
|
|
1388
|
-
this.cdRef.detectChanges();
|
|
1389
|
-
this.startCountdown(this.injector);
|
|
1235
|
+
calculateTargetMonth(currentMonth, monthsToSubtract) {
|
|
1236
|
+
return (currentMonth - monthsToSubtract + 12) % 12;
|
|
1390
1237
|
}
|
|
1391
1238
|
/**
|
|
1392
|
-
*
|
|
1239
|
+
* Sets the date to the last day of the previous month.
|
|
1240
|
+
* Using 0 as dateValue makes JavaScript automatically calculate
|
|
1241
|
+
* last day of previous month, per JavaScript Date API behavior.
|
|
1242
|
+
* @param date - Date to modify
|
|
1393
1243
|
*/
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
? this.translateService.instant(gettext('Disable auto refresh'))
|
|
1397
|
-
: this.translateService.instant(gettext('Enable auto refresh'));
|
|
1398
|
-
}
|
|
1399
|
-
canStartCountdown(changes) {
|
|
1400
|
-
const { isAutoRefreshEnabled, refreshInterval } = changes;
|
|
1401
|
-
return ((isAutoRefreshEnabled || refreshInterval) &&
|
|
1402
|
-
(this.isAutoRefreshEnabled || !!this.refreshInterval));
|
|
1403
|
-
}
|
|
1404
|
-
canHandleScrolling() {
|
|
1405
|
-
return !this.manuallyDisabledCountdown && this.isAutoRefreshEnabled;
|
|
1406
|
-
}
|
|
1407
|
-
canDisableCountdownWhenModalIsOpen() {
|
|
1408
|
-
return (this.isAutoRefreshEnabled &&
|
|
1409
|
-
!!this.refreshInterval &&
|
|
1410
|
-
!this.manuallyDisabledCountdown &&
|
|
1411
|
-
!this.isScrolling &&
|
|
1412
|
-
!this.hideCountdown);
|
|
1244
|
+
setToLastDayOfPreviousMonth(date) {
|
|
1245
|
+
date.setUTCDate(0);
|
|
1413
1246
|
}
|
|
1414
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type:
|
|
1415
|
-
static { this.ɵ
|
|
1247
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableViewService, deps: [{ token: i1.DataFetchingService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1248
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableViewService, providedIn: 'root' }); }
|
|
1416
1249
|
}
|
|
1417
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type:
|
|
1418
|
-
type:
|
|
1419
|
-
args: [{
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
}], isAutoRefreshEnabled: [{
|
|
1424
|
-
type: Input
|
|
1425
|
-
}], isDisabled: [{
|
|
1426
|
-
type: Input
|
|
1427
|
-
}], isLoading: [{
|
|
1428
|
-
type: Input
|
|
1429
|
-
}], isScrolling: [{
|
|
1430
|
-
type: Input
|
|
1431
|
-
}], isExportModalOpen: [{
|
|
1432
|
-
type: Input
|
|
1433
|
-
}], refreshInterval: [{
|
|
1434
|
-
type: Input
|
|
1435
|
-
}], onCountdownEnded: [{
|
|
1436
|
-
type: Output
|
|
1437
|
-
}] } });
|
|
1250
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableViewService, decorators: [{
|
|
1251
|
+
type: Injectable,
|
|
1252
|
+
args: [{
|
|
1253
|
+
providedIn: 'root'
|
|
1254
|
+
}]
|
|
1255
|
+
}], ctorParameters: () => [{ type: i1.DataFetchingService }] });
|
|
1438
1256
|
|
|
1439
1257
|
/**
|
|
1440
1258
|
* Creates a column header title message.
|
|
@@ -1697,7 +1515,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
|
|
|
1697
1515
|
}] } });
|
|
1698
1516
|
|
|
1699
1517
|
class DatapointsTableViewWidgetComponent {
|
|
1700
|
-
constructor(datapointsTableConfigService, datapointsTableViewService, formBuilder, translateService) {
|
|
1518
|
+
constructor(dataFetchingService, datapointsTableConfigService, datapointsTableViewService, formBuilder, translateService) {
|
|
1519
|
+
this.dataFetchingService = dataFetchingService;
|
|
1701
1520
|
this.datapointsTableConfigService = datapointsTableConfigService;
|
|
1702
1521
|
this.datapointsTableViewService = datapointsTableViewService;
|
|
1703
1522
|
this.formBuilder = formBuilder;
|
|
@@ -1736,9 +1555,15 @@ class DatapointsTableViewWidgetComponent {
|
|
|
1736
1555
|
// this.handleGlobalTimeContextSettings();
|
|
1737
1556
|
// }
|
|
1738
1557
|
// Condition will be removed in MTM-61920
|
|
1558
|
+
// Dashboard context is temporary not supported in the new implementation
|
|
1739
1559
|
if (this.config.widgetInstanceGlobalTimeContext) {
|
|
1740
1560
|
this.config.widgetInstanceGlobalTimeContext = false;
|
|
1741
1561
|
}
|
|
1562
|
+
// Condition will be removed in MTM-61920
|
|
1563
|
+
// Dashboard context is temporary not supported in the new implementation
|
|
1564
|
+
if (this.config['globalDateSelector'] === 'dashboard_context') {
|
|
1565
|
+
this.config['globalDateSelector'] = DATE_SELECTION_VALUES.config;
|
|
1566
|
+
}
|
|
1742
1567
|
this.setScrollingSubscription();
|
|
1743
1568
|
const isLegacyWidgetRealtimeActive = this.config.realtime &&
|
|
1744
1569
|
!this.config.decimalPlaces &&
|
|
@@ -1747,35 +1572,24 @@ class DatapointsTableViewWidgetComponent {
|
|
|
1747
1572
|
if (isLegacyWidgetRealtimeActive) {
|
|
1748
1573
|
this.setDefaultRefreshRelatedProperties();
|
|
1749
1574
|
}
|
|
1750
|
-
if (
|
|
1751
|
-
/**
|
|
1752
|
-
* Fix for legacy widget bug.
|
|
1753
|
-
* When new legacy widget is created and saved
|
|
1754
|
-
* then interval value in a config would stay null until interval is manually changed
|
|
1755
|
-
* and saved again.
|
|
1756
|
-
* Bug was introduced post 1018.0.164 version of UI.
|
|
1757
|
-
*/
|
|
1758
|
-
this.config.interval = 'hours';
|
|
1759
|
-
}
|
|
1760
|
-
if (this.config.interval !== TIME_RANGE_INTERVAL_UNITS_VALUES.custom) {
|
|
1575
|
+
if (this.config.interval !== INTERVAL_VALUES.custom) {
|
|
1761
1576
|
this.recalculateIntervalToMatchFromNowDate();
|
|
1762
1577
|
}
|
|
1763
1578
|
if (this.config.displayDateSelection) {
|
|
1764
1579
|
this.prepareDateRangeForm();
|
|
1765
1580
|
}
|
|
1766
1581
|
await this.prepareTableData();
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
// };
|
|
1582
|
+
this.exportConfig = {
|
|
1583
|
+
aggregation: this.config.aggregation,
|
|
1584
|
+
dateFrom: this.config.dateFrom,
|
|
1585
|
+
dateTo: this.config.dateTo,
|
|
1586
|
+
datapointDetails: this.activeDatapoints.map(({ __target, fragment, series }) => ({
|
|
1587
|
+
deviceName: __target.name,
|
|
1588
|
+
source: __target.id,
|
|
1589
|
+
valueFragmentSeries: series,
|
|
1590
|
+
valueFragmentType: fragment
|
|
1591
|
+
}))
|
|
1592
|
+
};
|
|
1779
1593
|
}
|
|
1780
1594
|
ngOnDestroy() {
|
|
1781
1595
|
if (this.subscription) {
|
|
@@ -1793,11 +1607,7 @@ class DatapointsTableViewWidgetComponent {
|
|
|
1793
1607
|
this.config.dateTo = data.dateTo;
|
|
1794
1608
|
}
|
|
1795
1609
|
this.prepareTableData();
|
|
1796
|
-
|
|
1797
|
-
// TODO-2: each manual/auto list reload should update exports config date range to sync current state of view with
|
|
1798
|
-
// inital export config
|
|
1799
|
-
// this.exportConfig.dateFrom = this.config.dateFrom;
|
|
1800
|
-
// this.exportConfig.dateTo = this.config.dateTo;
|
|
1610
|
+
this.updateExportConfigDateRange();
|
|
1801
1611
|
}
|
|
1802
1612
|
onExportModalOpen(isOpened) {
|
|
1803
1613
|
this.isExportModalOpen = isOpened;
|
|
@@ -1809,12 +1619,13 @@ class DatapointsTableViewWidgetComponent {
|
|
|
1809
1619
|
const dateRange = this.datapointsTableViewService.prepareTimeRange(this.config.interval, this.config.dateFrom, this.config.dateTo);
|
|
1810
1620
|
this.config.dateFrom = dateRange.dateFrom;
|
|
1811
1621
|
this.config.dateTo = dateRange.dateTo;
|
|
1622
|
+
this.updateExportConfigDateRange();
|
|
1812
1623
|
await this.prepareTableData(false);
|
|
1813
1624
|
this.onScrolling(false);
|
|
1814
1625
|
}
|
|
1815
1626
|
handleGlobalTimeContextSettings() {
|
|
1816
|
-
this.config.dateFrom = this.
|
|
1817
|
-
this.config.dateTo = this.
|
|
1627
|
+
this.config.dateFrom = this.dataFetchingService.adjustDate(this.config.date[0], 0, false);
|
|
1628
|
+
this.config.dateTo = this.dataFetchingService.adjustDate(this.config.date[1], 0, false);
|
|
1818
1629
|
if (this.config.realtime) {
|
|
1819
1630
|
this.setDefaultRefreshRelatedProperties();
|
|
1820
1631
|
}
|
|
@@ -1834,7 +1645,7 @@ class DatapointsTableViewWidgetComponent {
|
|
|
1834
1645
|
}
|
|
1835
1646
|
setDefaultRefreshRelatedProperties() {
|
|
1836
1647
|
this.config.isAutoRefreshEnabled = true;
|
|
1837
|
-
this.config.refreshInterval =
|
|
1648
|
+
this.config.refreshInterval = DEFAULT_DPT_REFRESH_INTERVAL_VALUE;
|
|
1838
1649
|
}
|
|
1839
1650
|
recalculateIntervalToMatchFromNowDate() {
|
|
1840
1651
|
if (this.config.widgetInstanceGlobalTimeContext) {
|
|
@@ -1939,24 +1750,35 @@ class DatapointsTableViewWidgetComponent {
|
|
|
1939
1750
|
</div>
|
|
1940
1751
|
`;
|
|
1941
1752
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1753
|
+
updateExportConfigDateRange() {
|
|
1754
|
+
this.config.dateTo = this.adjustTargetDateBasedOnConditionTime(this.config.dateFrom, this.config.dateTo);
|
|
1755
|
+
this.config.dateFrom = this.adjustTargetDateBasedOnConditionTime(this.config.dateTo, this.config.dateFrom);
|
|
1756
|
+
this.syncExportConfigDates();
|
|
1757
|
+
}
|
|
1758
|
+
adjustTargetDateBasedOnConditionTime(conditionDate, targetDate) {
|
|
1759
|
+
if (this.datapointsTableViewService.hasSecondsAndMillisecondsEqualZero(conditionDate)) {
|
|
1760
|
+
return this.dataFetchingService.adjustDate(targetDate, 0, true);
|
|
1761
|
+
}
|
|
1762
|
+
return targetDate;
|
|
1763
|
+
}
|
|
1764
|
+
syncExportConfigDates() {
|
|
1765
|
+
this.exportConfig.dateFrom = this.config.dateFrom;
|
|
1766
|
+
this.exportConfig.dateTo = this.config.dateTo;
|
|
1767
|
+
}
|
|
1768
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableViewWidgetComponent, deps: [{ token: i1.DataFetchingService }, { token: DatapointsTableService }, { token: DatapointsTableViewService }, { token: i3.FormBuilder }, { token: i1$2.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1769
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DatapointsTableViewWidgetComponent, isStandalone: true, selector: "c8y-datapoints-table-view", inputs: { config: "config" }, host: { classAttribute: "d-col fit-h" }, ngImport: i0, template: "<div class=\"d-flex gap-16 p-l-16 p-r-16 flex-wrap\">\n <ng-container *ngIf=\"config.displayDateSelection\">\n <form [formGroup]=\"formGroup\">\n <c8y-date-range-picker\n class=\"d-contents\"\n [isEmittingDateChange]=\"true\"\n [showLabel]=\"true\"\n (updatedDate)=\"onDateChange($event)\"\n ></c8y-date-range-picker>\n </form>\n </ng-container>\n <ng-container *ngIf=\"config.datapoints.length > 0\">\n <c8y-datapoints-reload\n class=\"d-contents\"\n [isAutoRefreshEnabled]=\"config.isAutoRefreshEnabled\"\n [refreshInterval]=\"config.refreshInterval\"\n [isDisabled]=\"isDisabled\"\n [isLoading]=\"isLoading\"\n [isScrolling]=\"isScrolling()\"\n [isExportModalOpen]=\"isExportModalOpen\"\n (onCountdownEnded)=\"onCountdownEnded()\"\n ></c8y-datapoints-reload>\n </ng-container>\n <c8y-datapoints-export-selector\n class=\"d-contents\"\n [exportConfig]=\"exportConfig\"\n (isOpen)=\"onExportModalOpen($event)\"\n ></c8y-datapoints-export-selector>\n</div>\n<ng-container *ngIf=\"devicesColumnHeaders\">\n <c8y-datapoints-table\n [aggregationType]=\"config.aggregation\"\n [datapointsTableItems]=\"datapointsTableItems\"\n [decimalPlaces]=\"config.decimalPlaces\"\n [devicesColumnHeaders]=\"devicesColumnHeaders\"\n [hasMultipleDatapoints]=\"hasMultipleDatapoints\"\n [isLoading]=\"isLoading()\"\n [seriesWithoutPermissionToReadCount]=\"seriesWithoutPermissionToRead?.length\"\n (isScrolling)=\"onScrolling($event)\"\n ></c8y-datapoints-table>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule$1 }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DatapointsExportSelectorComponent, selector: "c8y-datapoints-export-selector", inputs: ["exportConfig"], outputs: ["isOpen"] }, { kind: "component", type: DatapointsReloadComponent, selector: "c8y-datapoints-reload", inputs: ["isAutoRefreshEnabled", "isDisabled", "isLoading", "isScrolling", "isExportModalOpen", "refreshInterval"], outputs: ["onCountdownEnded"] }, { kind: "component", type: DatapointsTableComponent, selector: "c8y-datapoints-table", inputs: ["aggregationType", "datapointsTableItems", "devicesColumnHeaders", "decimalPlaces", "hasMultipleDatapoints", "isLoading", "seriesWithoutPermissionToReadCount"], outputs: ["isScrolling"] }, { kind: "component", type: DateRangePickerComponent, selector: "c8y-date-range-picker", inputs: ["isEmittingDateChange", "showLabel"], outputs: ["updatedDate"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] }); }
|
|
1947
1770
|
}
|
|
1948
1771
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsTableViewWidgetComponent, decorators: [{
|
|
1949
1772
|
type: Component,
|
|
1950
1773
|
args: [{ selector: 'c8y-datapoints-table-view', host: { class: 'd-col fit-h' }, standalone: true, imports: [
|
|
1951
1774
|
CommonModule$1,
|
|
1952
|
-
|
|
1953
|
-
// DatapointsExportSelectorComponent,
|
|
1775
|
+
DatapointsExportSelectorComponent,
|
|
1954
1776
|
DatapointsReloadComponent,
|
|
1955
1777
|
DatapointsTableComponent,
|
|
1956
1778
|
DateRangePickerComponent,
|
|
1957
1779
|
ReactiveFormsModule
|
|
1958
|
-
], template: "<div class=\"d-flex gap-16 p-l-16 p-r-16 flex-wrap\">\n <ng-container *ngIf=\"config.displayDateSelection\">\n <form [formGroup]=\"formGroup\">\n <c8y-date-range-picker\n class=\"d-contents\"\n [isEmittingDateChange]=\"true\"\n [showLabel]=\"true\"\n (updatedDate)=\"onDateChange($event)\"\n ></c8y-date-range-picker>\n </form>\n </ng-container>\n
|
|
1959
|
-
}], ctorParameters: () => [{ type: DatapointsTableService }, { type: DatapointsTableViewService }, { type: i3.FormBuilder }, { type: i1$2.TranslateService }], propDecorators: { config: [{
|
|
1780
|
+
], template: "<div class=\"d-flex gap-16 p-l-16 p-r-16 flex-wrap\">\n <ng-container *ngIf=\"config.displayDateSelection\">\n <form [formGroup]=\"formGroup\">\n <c8y-date-range-picker\n class=\"d-contents\"\n [isEmittingDateChange]=\"true\"\n [showLabel]=\"true\"\n (updatedDate)=\"onDateChange($event)\"\n ></c8y-date-range-picker>\n </form>\n </ng-container>\n <ng-container *ngIf=\"config.datapoints.length > 0\">\n <c8y-datapoints-reload\n class=\"d-contents\"\n [isAutoRefreshEnabled]=\"config.isAutoRefreshEnabled\"\n [refreshInterval]=\"config.refreshInterval\"\n [isDisabled]=\"isDisabled\"\n [isLoading]=\"isLoading\"\n [isScrolling]=\"isScrolling()\"\n [isExportModalOpen]=\"isExportModalOpen\"\n (onCountdownEnded)=\"onCountdownEnded()\"\n ></c8y-datapoints-reload>\n </ng-container>\n <c8y-datapoints-export-selector\n class=\"d-contents\"\n [exportConfig]=\"exportConfig\"\n (isOpen)=\"onExportModalOpen($event)\"\n ></c8y-datapoints-export-selector>\n</div>\n<ng-container *ngIf=\"devicesColumnHeaders\">\n <c8y-datapoints-table\n [aggregationType]=\"config.aggregation\"\n [datapointsTableItems]=\"datapointsTableItems\"\n [decimalPlaces]=\"config.decimalPlaces\"\n [devicesColumnHeaders]=\"devicesColumnHeaders\"\n [hasMultipleDatapoints]=\"hasMultipleDatapoints\"\n [isLoading]=\"isLoading()\"\n [seriesWithoutPermissionToReadCount]=\"seriesWithoutPermissionToRead?.length\"\n (isScrolling)=\"onScrolling($event)\"\n ></c8y-datapoints-table>\n</ng-container>\n" }]
|
|
1781
|
+
}], ctorParameters: () => [{ type: i1.DataFetchingService }, { type: DatapointsTableService }, { type: DatapointsTableViewService }, { type: i3.FormBuilder }, { type: i1$2.TranslateService }], propDecorators: { config: [{
|
|
1960
1782
|
type: Input
|
|
1961
1783
|
}] } });
|
|
1962
1784
|
|
|
@@ -1964,5 +1786,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
|
|
|
1964
1786
|
* Generated bundle index. Do not edit.
|
|
1965
1787
|
*/
|
|
1966
1788
|
|
|
1967
|
-
export { AdjustAggregatedTimeRangePipe, ApplyRangeClassPipe, DATE_SELECTION_LABELS, DATE_SELECTION_VALUES, DATE_SELECTION_VALUES_ARR,
|
|
1789
|
+
export { AdjustAggregatedTimeRangePipe, ApplyRangeClassPipe, DATE_SELECTION_LABELS, DATE_SELECTION_VALUES, DATE_SELECTION_VALUES_ARR, DEFAULT_DPT_REFRESH_INTERVAL_VALUE, DURATION_OPTIONS, DatapointsReloadComponent, DatapointsTableComponent, DatapointsTableViewService, DatapointsTableViewWidgetComponent, DatapointsTableWidgetConfigComponent, INTERVAL_VALUES_ARR, REFRESH_INTERVAL_VALUES_ARR, RENDER_TYPES_LABELS, TIME_RANGE_INTERVAL_LABELS, VirtualScrollListenerDirective, minOneDatapointActive };
|
|
1968
1790
|
//# sourceMappingURL=c8y-ngx-components-widgets-implementations-datapoints-table.mjs.map
|