@c8y/ngx-components 1023.63.1 → 1023.64.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.
Files changed (54) hide show
  1. package/datapoints-export-selector/index.d.ts +213 -48
  2. package/datapoints-export-selector/index.d.ts.map +1 -1
  3. package/echart/index.d.ts +1 -0
  4. package/echart/index.d.ts.map +1 -1
  5. package/fesm2022/c8y-ngx-components-datapoints-export-selector.mjs +568 -138
  6. package/fesm2022/c8y-ngx-components-datapoints-export-selector.mjs.map +1 -1
  7. package/fesm2022/c8y-ngx-components-echart.mjs +6 -3
  8. package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
  9. package/fesm2022/c8y-ngx-components-global-context.mjs +36 -1
  10. package/fesm2022/c8y-ngx-components-global-context.mjs.map +1 -1
  11. package/fesm2022/c8y-ngx-components-widgets-definitions-datapoints-list.mjs +80 -0
  12. package/fesm2022/c8y-ngx-components-widgets-definitions-datapoints-list.mjs.map +1 -0
  13. package/fesm2022/c8y-ngx-components-widgets-definitions.mjs +1 -0
  14. package/fesm2022/c8y-ngx-components-widgets-definitions.mjs.map +1 -1
  15. package/fesm2022/c8y-ngx-components-widgets-exports.mjs +8 -1
  16. package/fesm2022/c8y-ngx-components-widgets-exports.mjs.map +1 -1
  17. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-list.mjs +702 -0
  18. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-list.mjs.map +1 -0
  19. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs +3 -110
  20. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs.map +1 -1
  21. package/fesm2022/c8y-ngx-components.mjs +116 -6
  22. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  23. package/global-context/index.d.ts +2 -0
  24. package/global-context/index.d.ts.map +1 -1
  25. package/index.d.ts +59 -2
  26. package/index.d.ts.map +1 -1
  27. package/locales/de.po +94 -23
  28. package/locales/es.po +96 -23
  29. package/locales/fr.po +95 -23
  30. package/locales/ja_JP.po +82 -23
  31. package/locales/ko.po +97 -23
  32. package/locales/locales.pot +62 -11
  33. package/locales/nl.po +94 -23
  34. package/locales/pl.po +98 -23
  35. package/locales/pt_BR.po +97 -23
  36. package/locales/zh_CN.po +98 -23
  37. package/locales/zh_TW.po +98 -23
  38. package/package.json +1 -1
  39. package/widgets/cockpit-exports/index.d.ts +6 -0
  40. package/widgets/cockpit-exports/index.d.ts.map +1 -1
  41. package/widgets/definitions/datapoints-list/index.d.ts +51 -0
  42. package/widgets/definitions/datapoints-list/index.d.ts.map +1 -0
  43. package/widgets/definitions/index.d.ts +1 -0
  44. package/widgets/definitions/index.d.ts.map +1 -1
  45. package/widgets/device-management-exports/index.d.ts +6 -0
  46. package/widgets/device-management-exports/index.d.ts.map +1 -1
  47. package/widgets/exports/index.d.ts +8 -1
  48. package/widgets/exports/index.d.ts.map +1 -1
  49. package/widgets/implementations/alarms/index.d.ts +2 -0
  50. package/widgets/implementations/alarms/index.d.ts.map +1 -1
  51. package/widgets/implementations/datapoints-list/index.d.ts +286 -0
  52. package/widgets/implementations/datapoints-list/index.d.ts.map +1 -0
  53. package/widgets/implementations/datapoints-table/index.d.ts +5 -66
  54. package/widgets/implementations/datapoints-table/index.d.ts.map +1 -1
@@ -0,0 +1,702 @@
1
+ import { NgTemplateOutlet, DecimalPipe, AsyncPipe } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { Injectable, inject, input, linkedSignal, signal, computed, ChangeDetectionStrategy, Component, viewChild, DestroyRef } from '@angular/core';
4
+ import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
+ import { Router } from '@angular/router';
6
+ import { MeasurementService, InventoryService } from '@c8y/client';
7
+ import * as i1 from '@c8y/ngx-components';
8
+ import { AlertService, DashboardChildComponent, GroupService, DynamicComponentAlertAggregator, DynamicComponentAlert, C8yTranslateDirective, DeviceStatusComponent, DynamicComponentModule, EmptyStateComponent, ForOfDirective, GuideDocsComponent, GuideHrefDirective, IconDirective, ListGroupModule, LoadingComponent, VirtualScrollListenerDirective, WidgetActionWrapperComponent, ApplyRangeClassPipe, C8yTranslatePipe, DatePipe, FormGroupComponent } from '@c8y/ngx-components';
9
+ import { DatapointsExportSelectorComponent } from '@c8y/ngx-components/datapoints-export-selector';
10
+ import { gettext } from '@c8y/ngx-components/gettext';
11
+ import { WidgetConfigMigrationService, CONTEXT_FEATURE, GLOBAL_CONTEXT_DISPLAY_MODE, PRESET_NAME, REFRESH_OPTION, GlobalContextConnectorComponent, LocalControlsComponent } from '@c8y/ngx-components/global-context';
12
+ import { merge, isEqual } from 'lodash-es';
13
+ import { pairwise, filter, debounceTime, take, distinctUntilChanged } from 'rxjs';
14
+ import * as i1$1 from '@angular/cdk/drag-drop';
15
+ import { DragDropModule } from '@angular/cdk/drag-drop';
16
+ import * as i3 from '@angular/forms';
17
+ import { NgForm, FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
18
+ import { WidgetConfigService } from '@c8y/ngx-components/context-dashboard';
19
+ import * as i4 from 'ngx-bootstrap/popover';
20
+ import { PopoverModule } from 'ngx-bootstrap/popover';
21
+
22
+ /**
23
+ * Default column configuration for datapoints list widget
24
+ */
25
+ const DEFAULT_DATAPOINTS_LIST_COLUMNS = [
26
+ { id: 'kpi', label: gettext('Label'), visible: true, order: 0 },
27
+ { id: 'target', label: gettext('Target'), visible: true, order: 1 },
28
+ { id: 'current', label: gettext('Current'), visible: true, order: 2 },
29
+ { id: 'diff', label: gettext('Diff'), visible: true, order: 3 },
30
+ { id: 'diffPercentage', label: gettext('Diff %'), visible: true, order: 4 },
31
+ { id: 'asset', label: gettext('Asset'), visible: true, order: 5 }
32
+ ];
33
+
34
+ class DatapointsListService {
35
+ /**
36
+ * Calculate difference between current value and target
37
+ * @param datapoint - Datapoint record
38
+ * @returns Difference value or null if value/target is undefined
39
+ */
40
+ diff(datapoint) {
41
+ const { currentValue, target } = datapoint;
42
+ // != checks both null and undefined
43
+ if (currentValue != null && target != null) {
44
+ return currentValue - target;
45
+ }
46
+ return null;
47
+ }
48
+ /**
49
+ * Calculate percentage difference between current value and target
50
+ * @param datapoint - Datapoint record
51
+ * @returns Percentage difference or null if target is undefined
52
+ */
53
+ diffPercent(datapoint) {
54
+ const target = datapoint.target;
55
+ if (target !== null && target !== undefined) {
56
+ const _diff = this.diff(datapoint);
57
+ if (_diff !== null) {
58
+ // Intentionally allows division by zero to return Infinity
59
+ // when target is 0, representing mathematically undefined percentage.
60
+ // This follows the previous AngularJS implementation behavior.
61
+ return (_diff / target) * 100;
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+ /**
67
+ * Get fraction size format based on whether the value is an integer
68
+ * @param value - Number to check
69
+ * @param defaultFractionSize - Default fraction size format to use for non-integers
70
+ * @returns Fraction size format ('1.0-0' for integers, defaultFractionSize for decimals)
71
+ */
72
+ getFractionSize(value, defaultFractionSize) {
73
+ if (value === null || value === undefined) {
74
+ return defaultFractionSize;
75
+ }
76
+ return value % 1 === 0 ? '1.0-0' : defaultFractionSize;
77
+ }
78
+ /**
79
+ * Extract current value and timestamp from a measurement
80
+ * @param datapoint - Datapoint configuration (contains fragment and series)
81
+ * @param measurement - Measurement to extract value from
82
+ * @returns Object containing extracted value and timestamp (null if not found)
83
+ */
84
+ extractMeasurementValue(datapoint, measurement) {
85
+ // Return null values if measurement is missing
86
+ if (!measurement) {
87
+ return { value: null, timestamp: null };
88
+ }
89
+ // Return null values if datapoint configuration is incomplete
90
+ if (!datapoint.fragment || !datapoint.series) {
91
+ return { value: null, timestamp: null };
92
+ }
93
+ // Access the fragment (e.g., "c8y_Steam")
94
+ const fragmentData = measurement[datapoint.fragment];
95
+ if (!fragmentData) {
96
+ return { value: null, timestamp: null };
97
+ }
98
+ // Access the series within the fragment (e.g., "Temperature")
99
+ const seriesData = fragmentData[datapoint.series];
100
+ if (!seriesData || seriesData.value === undefined) {
101
+ return { value: null, timestamp: null };
102
+ }
103
+ // Extract and return the value and timestamp
104
+ return {
105
+ value: seriesData.value,
106
+ timestamp: new Date(measurement.time)
107
+ };
108
+ }
109
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatapointsListService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
110
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatapointsListService, providedIn: 'root' }); }
111
+ }
112
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatapointsListService, decorators: [{
113
+ type: Injectable,
114
+ args: [{
115
+ providedIn: 'root'
116
+ }]
117
+ }] });
118
+
119
+ /**
120
+ * Service that handles all data-fetching operations for the datapoints list widget.
121
+ * Encapsulates measurement fetching, data enrichment, and error handling.
122
+ */
123
+ class DatapointsListFetchService {
124
+ constructor() {
125
+ this.alertService = inject(AlertService);
126
+ this.measurementService = inject(MeasurementService);
127
+ this.inventoryService = inject(InventoryService);
128
+ this.datapointsListService = inject(DatapointsListService);
129
+ }
130
+ /**
131
+ * Fetch measurements for all active datapoints and enrich them with calculated values.
132
+ */
133
+ async fetchDatapointsWithMeasurements(datapoints, config) {
134
+ const targetManagedObjects = new Map();
135
+ const results = await this.fetchAllDatapoints(datapoints, config);
136
+ const enrichedDataPoints = results.map(r => r.datapoint);
137
+ const seriesWithoutPermissionCount = results.filter(r => r.hasPermissionError).length;
138
+ await this.fetchTargetManagedObjects(enrichedDataPoints, targetManagedObjects);
139
+ return {
140
+ dataPoints: enrichedDataPoints,
141
+ seriesWithoutPermissionCount,
142
+ targetManagedObjects
143
+ };
144
+ }
145
+ /**
146
+ * Fetch and enrich all datapoints in parallel.
147
+ * Each datapoint fetch is independent - one failure doesn't affect others.
148
+ */
149
+ fetchAllDatapoints(datapoints, config) {
150
+ return Promise.all(datapoints.map((datapoint, index) => this.fetchSingleDatapoint(datapoint, index, config)));
151
+ }
152
+ /**
153
+ * Fetch measurement for a single datapoint and return enriched result.
154
+ * Handles errors gracefully - returns empty datapoint on failure.
155
+ */
156
+ async fetchSingleDatapoint(datapoint, index, config) {
157
+ try {
158
+ const measurement = await this.getMeasurementForDatapoint(datapoint, config);
159
+ const measurementValue = this.datapointsListService.extractMeasurementValue(datapoint, measurement);
160
+ return {
161
+ datapoint: this.createEnrichedDatapoint(datapoint, index, measurementValue, config),
162
+ hasPermissionError: false
163
+ };
164
+ }
165
+ catch (error) {
166
+ return this.handleFetchError(error, datapoint, index, config);
167
+ }
168
+ }
169
+ /**
170
+ * Handle fetch error and return appropriate result.
171
+ */
172
+ handleFetchError(error, datapoint, index, config) {
173
+ const isPermissionError = error?.status === 403;
174
+ if (!isPermissionError) {
175
+ this.alertService.addServerFailure(error);
176
+ }
177
+ return {
178
+ datapoint: this.createEnrichedDatapoint(datapoint, index, { value: null, timestamp: null }, config),
179
+ hasPermissionError: isPermissionError
180
+ };
181
+ }
182
+ /**
183
+ * Create an enriched datapoint with measurement value and all calculated fields.
184
+ */
185
+ createEnrichedDatapoint(datapoint, index, measurementValue, config) {
186
+ const enrichedDatapoint = {
187
+ ...datapoint,
188
+ id: this.getDatapointId(datapoint, index),
189
+ currentValue: measurementValue.value,
190
+ timestamp: measurementValue.timestamp
191
+ };
192
+ this.calculateDerivedFields(enrichedDatapoint, config.fractionSize);
193
+ return enrichedDatapoint;
194
+ }
195
+ /**
196
+ * Calculate and set all derived fields on a datapoint.
197
+ * Includes diff, diffPercent, and fraction sizes for display formatting.
198
+ */
199
+ calculateDerivedFields(datapoint, fractionSize) {
200
+ datapoint.diffValue = this.datapointsListService.diff(datapoint);
201
+ datapoint.diffPercentValue = this.datapointsListService.diffPercent(datapoint);
202
+ datapoint.currentFractionSize = this.datapointsListService.getFractionSize(datapoint.currentValue, fractionSize);
203
+ datapoint.diffFractionSize = this.datapointsListService.getFractionSize(datapoint.diffValue, fractionSize);
204
+ datapoint.diffPercentFractionSize = this.datapointsListService.getFractionSize(datapoint.diffPercentValue, fractionSize);
205
+ }
206
+ /**
207
+ * Fetch managed objects for device status display.
208
+ */
209
+ async fetchTargetManagedObjects(dataPoints, targetManagedObjects) {
210
+ const uniqueTargetIds = this.getUniqueTargetIds(dataPoints);
211
+ if (uniqueTargetIds.size === 0) {
212
+ return;
213
+ }
214
+ await Promise.all(Array.from(uniqueTargetIds).map(targetId => this.fetchManagedObject(targetId, targetManagedObjects)));
215
+ }
216
+ /**
217
+ * Get unique target IDs from datapoints.
218
+ */
219
+ getUniqueTargetIds(dataPoints) {
220
+ const uniqueTargetIds = new Set();
221
+ for (const dp of dataPoints) {
222
+ if (dp.__target?.id) {
223
+ uniqueTargetIds.add(dp.__target.id.toString());
224
+ }
225
+ }
226
+ return uniqueTargetIds;
227
+ }
228
+ /**
229
+ * Fetch a single managed object and add to the map.
230
+ */
231
+ async fetchManagedObject(targetId, targetManagedObjects) {
232
+ try {
233
+ const { data } = await this.inventoryService.detail(targetId);
234
+ targetManagedObjects.set(targetId, data);
235
+ }
236
+ catch (error) {
237
+ this.alertService.addServerFailure(error);
238
+ }
239
+ }
240
+ /**
241
+ * Fetch the most recent measurement for a datapoint within the specified date range.
242
+ */
243
+ async getMeasurementForDatapoint(datapoint, config) {
244
+ const sourceId = datapoint.__target?.id;
245
+ if (!sourceId || !datapoint.fragment || !datapoint.series) {
246
+ return null;
247
+ }
248
+ const filter = {
249
+ dateFrom: config.dateFrom || '1970-01-01',
250
+ dateTo: config.dateTo || new Date().toISOString(),
251
+ source: sourceId,
252
+ valueFragmentSeries: datapoint.series,
253
+ valueFragmentType: datapoint.fragment,
254
+ pageSize: 1,
255
+ revert: true
256
+ };
257
+ const { data } = await this.measurementService.list(filter);
258
+ return data?.[0] ?? null;
259
+ }
260
+ /**
261
+ * Get unique ID for a datapoint.
262
+ * Uses target ID if available, otherwise generates fallback based on index.
263
+ */
264
+ getDatapointId(datapoint, fallbackIndex) {
265
+ return datapoint.__target?.id?.toString() || `dp-${fallbackIndex}`;
266
+ }
267
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatapointsListFetchService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
268
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatapointsListFetchService, providedIn: 'root' }); }
269
+ }
270
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatapointsListFetchService, decorators: [{
271
+ type: Injectable,
272
+ args: [{
273
+ providedIn: 'root'
274
+ }]
275
+ }] });
276
+
277
+ class DatapointsListViewComponent {
278
+ constructor() {
279
+ this.config = input.required(...(ngDevMode ? [{ debugName: "config" }] : []));
280
+ this.isInPreviewMode = input(false, ...(ngDevMode ? [{ debugName: "isInPreviewMode" }] : []));
281
+ this.alertService = inject(AlertService);
282
+ this.dashboardChild = inject(DashboardChildComponent, { optional: true });
283
+ this.defaultColumns = DEFAULT_DATAPOINTS_LIST_COLUMNS;
284
+ this.fetchService = inject(DatapointsListFetchService);
285
+ this.groupService = inject(GroupService);
286
+ this.inventoryService = inject(InventoryService);
287
+ this.router = inject(Router);
288
+ this.widgetConfigMigrationService = inject(WidgetConfigMigrationService);
289
+ this.CONTEXT_FEATURE = CONTEXT_FEATURE;
290
+ this.GLOBAL_CONTEXT_DISPLAY_MODE = GLOBAL_CONTEXT_DISPLAY_MODE;
291
+ this.missingAllPermissionsAlert = new DynamicComponentAlertAggregator();
292
+ this.targetManagedObjects = new Map();
293
+ this.configSignal = linkedSignal(() => {
294
+ const raw = this.config();
295
+ const migrated = this.widgetConfigMigrationService.migrateWidgetConfig(raw);
296
+ return merge({}, raw, migrated);
297
+ }, ...(ngDevMode ? [{ debugName: "configSignal" }] : []));
298
+ this.contextConfig = signal({}, ...(ngDevMode ? [{ debugName: "contextConfig" }] : []));
299
+ this.dataPoints = signal([], ...(ngDevMode ? [{ debugName: "dataPoints" }] : []));
300
+ this.displayMode = signal(GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD, ...(ngDevMode ? [{ debugName: "displayMode" }] : []));
301
+ this.hasLoadedOnce = signal(false, ...(ngDevMode ? [{ debugName: "hasLoadedOnce" }] : []));
302
+ this.hasNoPermissionsToReadAnyMeasurement = signal(false, ...(ngDevMode ? [{ debugName: "hasNoPermissionsToReadAnyMeasurement" }] : []));
303
+ this.isLinkedToGlobal = signal(undefined, ...(ngDevMode ? [{ debugName: "isLinkedToGlobal" }] : []));
304
+ this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
305
+ this.widgetControls = signal(PRESET_NAME.DATA_POINTS_LIST, ...(ngDevMode ? [{ debugName: "widgetControls" }] : []));
306
+ this.fractionSize = computed(() => {
307
+ const decimalPlaces = this.configSignal()?.decimalPlaces;
308
+ if (typeof decimalPlaces === 'number' && !Number.isNaN(decimalPlaces)) {
309
+ return `1.${decimalPlaces}-${decimalPlaces}`;
310
+ }
311
+ return '1.2-2';
312
+ }, ...(ngDevMode ? [{ debugName: "fractionSize" }] : []));
313
+ this.columns = computed(() => {
314
+ const options = this.configSignal()?.options;
315
+ if (options?.columns?.length > 0) {
316
+ return [...options.columns].sort((a, b) => a.order - b.order);
317
+ }
318
+ return this.defaultColumns.map(defaultCol => ({
319
+ ...defaultCol,
320
+ visible: options?.[defaultCol.id] ?? defaultCol.visible
321
+ }));
322
+ }, ...(ngDevMode ? [{ debugName: "columns" }] : []));
323
+ this.visibleColumns = computed(() => {
324
+ return this.columns().filter(col => col.visible !== false);
325
+ }, ...(ngDevMode ? [{ debugName: "visibleColumns" }] : []));
326
+ this.exportConfig = computed(() => {
327
+ const effectiveConfig = { ...this.configSignal(), ...this.contextConfig() };
328
+ const dateFrom = effectiveConfig.dateTimeContext?.dateFrom;
329
+ const dateTo = effectiveConfig.dateTimeContext?.dateTo;
330
+ if (!dateFrom || !dateTo || (this.isLoading() && !this.hasLoadedOnce())) {
331
+ return null;
332
+ }
333
+ return {
334
+ exportType: 'latestWithDetails',
335
+ datapointDetails: effectiveConfig.datapoints
336
+ ?.filter(dp => dp.__active === true)
337
+ .map(dp => ({
338
+ deviceName: dp.__target?.name || '',
339
+ source: dp.__target?.id || '',
340
+ valueFragmentSeries: dp.series,
341
+ valueFragmentType: dp.fragment,
342
+ target: dp.target,
343
+ label: dp.label
344
+ })) || [],
345
+ dateFrom: dateFrom instanceof Date ? dateFrom.toISOString() : dateFrom,
346
+ dateTo: dateTo instanceof Date ? dateTo.toISOString() : dateTo,
347
+ columns: this.columns()
348
+ .filter(col => col.visible)
349
+ .map(col => ({
350
+ id: col.id,
351
+ label: col.label,
352
+ visible: col.visible,
353
+ order: col.order
354
+ }))
355
+ };
356
+ }, ...(ngDevMode ? [{ debugName: "exportConfig" }] : []));
357
+ this.activeDataPoints = computed(() => {
358
+ return this.configSignal()?.datapoints?.filter(dp => dp.__active === true) ?? [];
359
+ }, ...(ngDevMode ? [{ debugName: "activeDataPoints" }] : []));
360
+ this.loadRequestId = 0;
361
+ this.seriesWithoutPermissionToReadCount = signal(0, ...(ngDevMode ? [{ debugName: "seriesWithoutPermissionToReadCount" }] : []));
362
+ toObservable(this.config)
363
+ .pipe(pairwise(), takeUntilDestroyed())
364
+ .subscribe(([prevConfig, currentConfig]) => {
365
+ if (!this.isInPreviewMode()) {
366
+ return;
367
+ }
368
+ const prevContext = this.extractContextState(prevConfig);
369
+ const newContext = this.extractContextState(currentConfig);
370
+ this.contextConfig.set(newContext);
371
+ const datapointsChanged = !isEqual(prevConfig.datapoints, currentConfig.datapoints);
372
+ const decimalPlacesChanged = prevConfig.decimalPlaces !== currentConfig.decimalPlaces;
373
+ const contextChanged = !isEqual(prevContext, newContext);
374
+ if (datapointsChanged || decimalPlacesChanged || contextChanged) {
375
+ this.isLoading.set(true);
376
+ this.loadDatapoints();
377
+ }
378
+ });
379
+ }
380
+ ngOnInit() {
381
+ const config = this.configSignal();
382
+ const displayMode = config.displayMode || GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD;
383
+ this.displayMode.set(displayMode);
384
+ this.contextConfig.set(this.extractContextState(config));
385
+ if (displayMode !== GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD || this.isInPreviewMode()) {
386
+ this.loadDatapoints();
387
+ }
388
+ }
389
+ onContextChange(event) {
390
+ const { diff, context } = event;
391
+ if (diff.isAutoRefreshEnabled === false &&
392
+ Object.keys(diff).length === 1 &&
393
+ context.refreshOption === REFRESH_OPTION.LIVE) {
394
+ return;
395
+ }
396
+ this.contextConfig.set(context);
397
+ this.isLoading.set(true);
398
+ this.loadDatapoints();
399
+ }
400
+ onRefresh() {
401
+ this.isLoading.set(true);
402
+ this.loadDatapoints();
403
+ }
404
+ onExportModalOpen(isOpened) {
405
+ this.setAutoRefreshPaused(isOpened);
406
+ }
407
+ async redirectToAsset(assetId) {
408
+ if (this.isInPreviewMode() || !assetId) {
409
+ return;
410
+ }
411
+ try {
412
+ const { data: mo } = await this.inventoryService.detail(assetId);
413
+ if (mo) {
414
+ const assetPath = this.groupService.getAssetPath(mo);
415
+ this.router.navigateByUrl(`/${assetPath}/${mo.id}`);
416
+ }
417
+ }
418
+ catch (error) {
419
+ this.alertService.addServerFailure(error);
420
+ }
421
+ }
422
+ getTargetManagedObject(targetId) {
423
+ return this.targetManagedObjects.get(targetId.toString());
424
+ }
425
+ getDashboardChild() {
426
+ return this.dashboardChild ?? null;
427
+ }
428
+ getRangeValues(dp) {
429
+ return {
430
+ yellowRangeMin: dp.yellowRangeMin,
431
+ yellowRangeMax: dp.yellowRangeMax,
432
+ redRangeMin: dp.redRangeMin,
433
+ redRangeMax: dp.redRangeMax
434
+ };
435
+ }
436
+ onListScrolled() {
437
+ this.setAutoRefreshPaused(true);
438
+ }
439
+ onListScrolledToTop() {
440
+ this.setAutoRefreshPaused(false);
441
+ }
442
+ extractContextState(config) {
443
+ return {
444
+ dateTimeContext: config.dateTimeContext,
445
+ aggregation: config.aggregation,
446
+ isAutoRefreshEnabled: config.isAutoRefreshEnabled,
447
+ refreshInterval: config.refreshInterval,
448
+ refreshOption: config.refreshOption
449
+ };
450
+ }
451
+ setAutoRefreshPaused(paused) {
452
+ if (this.isInPreviewMode()) {
453
+ return;
454
+ }
455
+ const current = this.contextConfig();
456
+ if (current.refreshOption === REFRESH_OPTION.HISTORY) {
457
+ return;
458
+ }
459
+ this.contextConfig.set({
460
+ ...current,
461
+ isAutoRefreshEnabled: !paused
462
+ });
463
+ this.isLinkedToGlobal.set(!paused);
464
+ }
465
+ loadDatapoints() {
466
+ if (this.activeDataPoints().length > 0) {
467
+ this.fetchMeasurements();
468
+ }
469
+ else {
470
+ this.dataPoints.set([]);
471
+ this.hasLoadedOnce.set(true);
472
+ this.isLoading.set(false);
473
+ }
474
+ }
475
+ async fetchMeasurements() {
476
+ const requestId = ++this.loadRequestId;
477
+ try {
478
+ this.isLoading.set(true);
479
+ const effectiveConfig = {
480
+ ...this.configSignal(),
481
+ ...this.contextConfig()
482
+ };
483
+ const dateFrom = effectiveConfig.dateTimeContext?.dateFrom;
484
+ const dateTo = effectiveConfig.dateTimeContext?.dateTo;
485
+ const dateFromStr = dateFrom instanceof Date ? dateFrom.toISOString() : dateFrom;
486
+ const dateToStr = dateTo instanceof Date ? dateTo.toISOString() : dateTo;
487
+ const result = await this.fetchService.fetchDatapointsWithMeasurements(this.activeDataPoints(), { fractionSize: this.fractionSize(), dateFrom: dateFromStr, dateTo: dateToStr });
488
+ if (requestId === this.loadRequestId) {
489
+ this.dataPoints.set(result.dataPoints);
490
+ this.targetManagedObjects = result.targetManagedObjects;
491
+ this.hasLoadedOnce.set(true);
492
+ this.seriesWithoutPermissionToReadCount.set(result.seriesWithoutPermissionCount);
493
+ this.checkAndDisplayPermissionErrors();
494
+ }
495
+ }
496
+ finally {
497
+ if (requestId === this.loadRequestId) {
498
+ this.isLoading.set(false);
499
+ }
500
+ }
501
+ }
502
+ checkAndDisplayPermissionErrors() {
503
+ if (this.seriesWithoutPermissionToReadCount()) {
504
+ this.missingAllPermissionsAlert.clear();
505
+ this.handleNoPermissionErrorMessage();
506
+ }
507
+ }
508
+ handleNoPermissionErrorMessage() {
509
+ const noPermissions = this.seriesWithoutPermissionToReadCount() === this.activeDataPoints().length;
510
+ this.hasNoPermissionsToReadAnyMeasurement.set(noPermissions);
511
+ if (noPermissions) {
512
+ this.showMessageForMissingPermissionsForAllSeries();
513
+ }
514
+ }
515
+ showMessageForMissingPermissionsForAllSeries() {
516
+ this.missingAllPermissionsAlert.addAlerts(new DynamicComponentAlert({
517
+ allowHtml: true,
518
+ text: gettext(`<p>To view data, you must meet at least one of these criteria:</p>
519
+ <ul>
520
+ <li>
521
+ Have
522
+ <b>READ permission for "Measurements" permission type</b>
523
+ (either as a global role or for the specific source)
524
+ </li>
525
+ <li>
526
+ Be the
527
+ <b>owner of the source</b>
528
+ you want to export data from
529
+ </li>
530
+ </ul>
531
+ <p>Don't meet these requirements? Contact your system administrator for assistance.</p>`),
532
+ type: 'system'
533
+ }));
534
+ }
535
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatapointsListViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
536
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: DatapointsListViewComponent, isStandalone: true, selector: "c8y-datapoints-list", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, isInPreviewMode: { classPropertyName: "isInPreviewMode", publicName: "isInPreviewMode", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "d-col fit-h" }, ngImport: i0, template: "@if (!isInPreviewMode()) {\n <div class=\"d-flex gap-16 p-r-16 inner-scroll h-auto min-width-0\">\n @if (displayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {\n <c8y-global-context-connector\n [controls]=\"widgetControls()\"\n [config]=\"contextConfig()\"\n [isLoading]=\"isLoading()\"\n [dashboardChild]=\"getDashboardChild()!\"\n [linked]=\"isLinkedToGlobal()\"\n (configChange)=\"onContextChange($event)\"\n (refresh)=\"onRefresh()\"\n ></c8y-global-context-connector>\n } @else {\n <c8y-local-controls\n [controls]=\"widgetControls()\"\n [displayMode]=\"displayMode()\"\n [config]=\"contextConfig()\"\n [isLoading]=\"isLoading()\"\n (configChange)=\"onContextChange($event)\"\n (refresh)=\"onRefresh()\"\n ></c8y-local-controls>\n }\n </div>\n}\n\n@if (!isInPreviewMode() && exportConfig(); as config) {\n <c8y-widget-action>\n <c8y-datapoints-export-selector\n [displayMode]=\"'icon-only'\"\n [exportConfig]=\"config\"\n [containerClass]=\"'d-contents'\"\n (isOpen)=\"onExportModalOpen($event)\"\n ></c8y-datapoints-export-selector>\n </c8y-widget-action>\n}\n\n@if (!hasNoPermissionsToReadAnyMeasurement()) {\n <!-- the .page-sticky-header -->\n @if (dataPoints().length > 0) {\n <div class=\"hidden-xs hidden-sm c8y-list__item\">\n <div class=\"c8y-list__item__block flex-grow min-width-0\">\n <div class=\"c8y-list__item__icon\">\n <i style=\"width: 22px\"></i>\n </div>\n <div class=\"c8y-list__item__body\">\n <div class=\"d-flex-md row\">\n @for (column of visibleColumns(); track column.id) {\n <div\n [class]=\"\n column.id === 'kpi' || column.id === 'asset'\n ? 'col-md-3 flex-grow min-width-0'\n : column.id == 'diff' || column.id == 'diffPercentage'\n ? 'col-md-1 flex-grow min-width-0'\n : 'col-md-2 flex-grow min-width-0'\n \"\n [class.text-right]=\"column.id !== 'asset' && column.id !== 'kpi'\"\n >\n <span\n class=\"text-medium text-truncate\"\n translate\n >\n {{ column.label }}\n </span>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n }\n <!-- The record list -->\n @if (isLoading() && !hasLoadedOnce()) {\n <!-- Initial load: full spinner -->\n <ng-container [ngTemplateOutlet]=\"loading\"></ng-container>\n } @else {\n @if (isLoading()) {\n <!-- Refresh: inline loading overlay -->\n <div class=\"p-absolute fit-w overflow-hidden p-b-4\">\n <c8y-loading [layout]=\"'page'\"></c8y-loading>\n </div>\n }\n @if (dataPoints().length) {\n <c8y-list-group\n class=\"flex-grow\"\n role=\"list\"\n c8yVirtualScrollListener\n (scrolled)=\"onListScrolled()\"\n (scrolledToTop)=\"onListScrolledToTop()\"\n >\n <c8y-li\n role=\"listitem\"\n *c8yFor=\"\n let dp of { data: dataPoints(), res: null! };\n enableVirtualScroll: true;\n virtualScrollElementSize: 48;\n virtualScrollStrategy: 'fixed'\n \"\n >\n <c8y-li-icon>\n <i\n c8yIcon=\"circle\"\n [style.color]=\"dp.color\"\n ></i>\n </c8y-li-icon>\n <c8y-li-body>\n <div class=\"d-flex-md row\">\n @for (column of visibleColumns(); track column.id) {\n @switch (column.id) {\n @case ('kpi') {\n <div\n class=\"col-md-3 flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n {{ dp.label | translate }}\n @if (dp.unit) {\n <small class=\"text-muted\">{{ dp.unit }}</small>\n }\n </div>\n </div>\n }\n @case ('target') {\n <div\n class=\"col-md-2 text-right-md flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n <span>{{ dp.target }}</span>\n </div>\n </div>\n }\n @case ('current') {\n <div\n class=\"col-md-2 text-right-md flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n <span\n [class]=\"dp.currentValue | applyRangeClass: getRangeValues(dp)\"\n [title]=\"dp.timestamp | c8yDate: 'medium'\"\n [attr.aria-label]=\"\n ('Last updated: ' | translate) + (dp.timestamp | c8yDate: 'medium')\n \"\n >\n {{ dp.currentValue | number: dp.currentFractionSize }}\n </span>\n </div>\n </div>\n }\n @case ('diff') {\n <div\n class=\"col-md-1 text-right-md flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n <span>\n {{ dp.diffValue | number: dp.diffFractionSize }}\n </span>\n </div>\n </div>\n }\n @case ('diffPercentage') {\n <div\n class=\"col-md-1 text-right-md flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n <span>\n {{ dp.diffPercentValue | number: dp.diffPercentFractionSize }}\n </span>\n </div>\n </div>\n }\n @case ('asset') {\n <div\n class=\"col-md-3 flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n <button\n class=\"btn-clean d-flex a-i-center gap-4 text-muted\"\n [attr.aria-label]=\"('Navigate to ' | translate) + dp.__target?.name\"\n type=\"button\"\n (click)=\"redirectToAsset(dp.__target?.id)\"\n >\n @if (dp.__target?.id && getTargetManagedObject(dp.__target.id); as mo) {\n <c8y-device-status\n [mo]=\"mo\"\n [size]=\"16\"\n ></c8y-device-status>\n }\n <small\n class=\"text-truncate\"\n [title]=\"dp.__target?.name\"\n >{{ dp.__target?.name }}</small\n >\n </button>\n </div>\n </div>\n }\n }\n }\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-list-group>\n } @else {\n <ng-container [ngTemplateOutlet]=\"emptyState\"></ng-container>\n }\n }\n} @else {\n <div class=\"p-t-24 p-r-16 p-l-16 p-b-16 d-flex\">\n <div class=\"center-block\">\n <c8y-dynamic-component-alerts\n [alerts]=\"missingAllPermissionsAlert\"\n ></c8y-dynamic-component-alerts>\n </div>\n </div>\n}\n\n<ng-template #loading>\n <c8y-loading></c8y-loading>\n</ng-template>\n\n<ng-template #emptyState>\n <div class=\"p-relative p-l-24\">\n <c8y-ui-empty-state\n [icon]=\"'c8y-alert-idle'\"\n [title]=\"'No data to display.' | translate\"\n [horizontal]=\"true\"\n data-cy=\"datapoints-list--empty-state\"\n >\n <p c8y-guide-docs>\n <small translate>\n Find out more in the\n <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#data-point-list\">\n user documentation\n </a>\n .\n </small>\n </p>\n </c8y-ui-empty-state>\n </div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: DatapointsExportSelectorComponent, selector: "c8y-datapoints-export-selector", inputs: ["displayMode", "containerClass", "exportConfig"], outputs: ["isOpen"] }, { kind: "component", type: DeviceStatusComponent, selector: "device-status, c8y-device-status", inputs: ["mo", "size"] }, { kind: "ngmodule", type: DynamicComponentModule }, { kind: "component", type: i1.DynamicComponentAlertsComponent, selector: "c8y-dynamic-component-alerts", inputs: ["alerts"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: ForOfDirective, selector: "[c8yFor]", inputs: ["c8yForOf", "c8yForLoadMore", "c8yForPipe", "c8yForNotFound", "c8yForMaxIterations", "c8yForLoadingTemplate", "c8yForLoadNextLabel", "c8yForLoadingLabel", "c8yForRealtime", "c8yForRealtimeOptions", "c8yForComparator", "c8yForEnableVirtualScroll", "c8yForVirtualScrollElementSize", "c8yForVirtualScrollStrategy", "c8yForVirtualScrollContainerHeight"], outputs: ["c8yForCount", "c8yForChange", "c8yForLoadMoreComponent"] }, { kind: "component", type: GlobalContextConnectorComponent, selector: "c8y-global-context-connector", inputs: ["controls", "config", "isLoading", "dashboardChild", "linked", "emitRefresh"], outputs: ["configChange", "refresh", "linkedChange"] }, { kind: "component", type: GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "directive", type: GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: ListGroupModule }, { kind: "component", type: i1.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i1.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i1.ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: i1.ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: LocalControlsComponent, selector: "c8y-local-controls", inputs: ["controls", "displayMode", "config", "isLoading", "disabled"], outputs: ["configChange", "refresh"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: VirtualScrollListenerDirective, selector: "[c8yVirtualScrollListener]", inputs: ["scrollThreshold"], outputs: ["scrolled", "scrolledToTop"] }, { kind: "component", type: WidgetActionWrapperComponent, selector: "c8y-widget-action" }, { kind: "pipe", type: ApplyRangeClassPipe, name: "applyRangeClass" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }, { kind: "pipe", type: DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
537
+ }
538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatapointsListViewComponent, decorators: [{
539
+ type: Component,
540
+ args: [{ selector: 'c8y-datapoints-list', host: { class: 'd-col fit-h' }, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
541
+ ApplyRangeClassPipe,
542
+ C8yTranslateDirective,
543
+ C8yTranslatePipe,
544
+ DatapointsExportSelectorComponent,
545
+ DatePipe,
546
+ DecimalPipe,
547
+ DeviceStatusComponent,
548
+ DynamicComponentModule,
549
+ EmptyStateComponent,
550
+ ForOfDirective,
551
+ GlobalContextConnectorComponent,
552
+ GuideDocsComponent,
553
+ GuideHrefDirective,
554
+ IconDirective,
555
+ ListGroupModule,
556
+ LoadingComponent,
557
+ LocalControlsComponent,
558
+ NgTemplateOutlet,
559
+ VirtualScrollListenerDirective,
560
+ WidgetActionWrapperComponent
561
+ ], template: "@if (!isInPreviewMode()) {\n <div class=\"d-flex gap-16 p-r-16 inner-scroll h-auto min-width-0\">\n @if (displayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {\n <c8y-global-context-connector\n [controls]=\"widgetControls()\"\n [config]=\"contextConfig()\"\n [isLoading]=\"isLoading()\"\n [dashboardChild]=\"getDashboardChild()!\"\n [linked]=\"isLinkedToGlobal()\"\n (configChange)=\"onContextChange($event)\"\n (refresh)=\"onRefresh()\"\n ></c8y-global-context-connector>\n } @else {\n <c8y-local-controls\n [controls]=\"widgetControls()\"\n [displayMode]=\"displayMode()\"\n [config]=\"contextConfig()\"\n [isLoading]=\"isLoading()\"\n (configChange)=\"onContextChange($event)\"\n (refresh)=\"onRefresh()\"\n ></c8y-local-controls>\n }\n </div>\n}\n\n@if (!isInPreviewMode() && exportConfig(); as config) {\n <c8y-widget-action>\n <c8y-datapoints-export-selector\n [displayMode]=\"'icon-only'\"\n [exportConfig]=\"config\"\n [containerClass]=\"'d-contents'\"\n (isOpen)=\"onExportModalOpen($event)\"\n ></c8y-datapoints-export-selector>\n </c8y-widget-action>\n}\n\n@if (!hasNoPermissionsToReadAnyMeasurement()) {\n <!-- the .page-sticky-header -->\n @if (dataPoints().length > 0) {\n <div class=\"hidden-xs hidden-sm c8y-list__item\">\n <div class=\"c8y-list__item__block flex-grow min-width-0\">\n <div class=\"c8y-list__item__icon\">\n <i style=\"width: 22px\"></i>\n </div>\n <div class=\"c8y-list__item__body\">\n <div class=\"d-flex-md row\">\n @for (column of visibleColumns(); track column.id) {\n <div\n [class]=\"\n column.id === 'kpi' || column.id === 'asset'\n ? 'col-md-3 flex-grow min-width-0'\n : column.id == 'diff' || column.id == 'diffPercentage'\n ? 'col-md-1 flex-grow min-width-0'\n : 'col-md-2 flex-grow min-width-0'\n \"\n [class.text-right]=\"column.id !== 'asset' && column.id !== 'kpi'\"\n >\n <span\n class=\"text-medium text-truncate\"\n translate\n >\n {{ column.label }}\n </span>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n }\n <!-- The record list -->\n @if (isLoading() && !hasLoadedOnce()) {\n <!-- Initial load: full spinner -->\n <ng-container [ngTemplateOutlet]=\"loading\"></ng-container>\n } @else {\n @if (isLoading()) {\n <!-- Refresh: inline loading overlay -->\n <div class=\"p-absolute fit-w overflow-hidden p-b-4\">\n <c8y-loading [layout]=\"'page'\"></c8y-loading>\n </div>\n }\n @if (dataPoints().length) {\n <c8y-list-group\n class=\"flex-grow\"\n role=\"list\"\n c8yVirtualScrollListener\n (scrolled)=\"onListScrolled()\"\n (scrolledToTop)=\"onListScrolledToTop()\"\n >\n <c8y-li\n role=\"listitem\"\n *c8yFor=\"\n let dp of { data: dataPoints(), res: null! };\n enableVirtualScroll: true;\n virtualScrollElementSize: 48;\n virtualScrollStrategy: 'fixed'\n \"\n >\n <c8y-li-icon>\n <i\n c8yIcon=\"circle\"\n [style.color]=\"dp.color\"\n ></i>\n </c8y-li-icon>\n <c8y-li-body>\n <div class=\"d-flex-md row\">\n @for (column of visibleColumns(); track column.id) {\n @switch (column.id) {\n @case ('kpi') {\n <div\n class=\"col-md-3 flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n {{ dp.label | translate }}\n @if (dp.unit) {\n <small class=\"text-muted\">{{ dp.unit }}</small>\n }\n </div>\n </div>\n }\n @case ('target') {\n <div\n class=\"col-md-2 text-right-md flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n <span>{{ dp.target }}</span>\n </div>\n </div>\n }\n @case ('current') {\n <div\n class=\"col-md-2 text-right-md flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n <span\n [class]=\"dp.currentValue | applyRangeClass: getRangeValues(dp)\"\n [title]=\"dp.timestamp | c8yDate: 'medium'\"\n [attr.aria-label]=\"\n ('Last updated: ' | translate) + (dp.timestamp | c8yDate: 'medium')\n \"\n >\n {{ dp.currentValue | number: dp.currentFractionSize }}\n </span>\n </div>\n </div>\n }\n @case ('diff') {\n <div\n class=\"col-md-1 text-right-md flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n <span>\n {{ dp.diffValue | number: dp.diffFractionSize }}\n </span>\n </div>\n </div>\n }\n @case ('diffPercentage') {\n <div\n class=\"col-md-1 text-right-md flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n <span>\n {{ dp.diffPercentValue | number: dp.diffPercentFractionSize }}\n </span>\n </div>\n </div>\n }\n @case ('asset') {\n <div\n class=\"col-md-3 flex-grow\"\n [attr.data-cy]=\"'datapointlist-' + column.id\"\n >\n <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n <small\n class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n >{{ column.label }}</small\n >\n <button\n class=\"btn-clean d-flex a-i-center gap-4 text-muted\"\n [attr.aria-label]=\"('Navigate to ' | translate) + dp.__target?.name\"\n type=\"button\"\n (click)=\"redirectToAsset(dp.__target?.id)\"\n >\n @if (dp.__target?.id && getTargetManagedObject(dp.__target.id); as mo) {\n <c8y-device-status\n [mo]=\"mo\"\n [size]=\"16\"\n ></c8y-device-status>\n }\n <small\n class=\"text-truncate\"\n [title]=\"dp.__target?.name\"\n >{{ dp.__target?.name }}</small\n >\n </button>\n </div>\n </div>\n }\n }\n }\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-list-group>\n } @else {\n <ng-container [ngTemplateOutlet]=\"emptyState\"></ng-container>\n }\n }\n} @else {\n <div class=\"p-t-24 p-r-16 p-l-16 p-b-16 d-flex\">\n <div class=\"center-block\">\n <c8y-dynamic-component-alerts\n [alerts]=\"missingAllPermissionsAlert\"\n ></c8y-dynamic-component-alerts>\n </div>\n </div>\n}\n\n<ng-template #loading>\n <c8y-loading></c8y-loading>\n</ng-template>\n\n<ng-template #emptyState>\n <div class=\"p-relative p-l-24\">\n <c8y-ui-empty-state\n [icon]=\"'c8y-alert-idle'\"\n [title]=\"'No data to display.' | translate\"\n [horizontal]=\"true\"\n data-cy=\"datapoints-list--empty-state\"\n >\n <p c8y-guide-docs>\n <small translate>\n Find out more in the\n <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#data-point-list\">\n user documentation\n </a>\n .\n </small>\n </p>\n </c8y-ui-empty-state>\n </div>\n</ng-template>\n" }]
562
+ }], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], isInPreviewMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "isInPreviewMode", required: false }] }] } });
563
+
564
+ const DEFAULT_DECIMAL_PLACES = 2;
565
+ const MIN_DECIMAL_PLACES = 0;
566
+ const MAX_DECIMAL_PLACES = 10;
567
+ class DatapointsListConfigComponent {
568
+ constructor() {
569
+ this.config = input.required(...(ngDevMode ? [{ debugName: "config" }] : []));
570
+ this.previewTemplate = viewChild('dataPointsListPreview', ...(ngDevMode ? [{ debugName: "previewTemplate" }] : []));
571
+ this.defaultColumns = DEFAULT_DATAPOINTS_LIST_COLUMNS;
572
+ this.destroyRef = inject(DestroyRef);
573
+ this.form = inject(NgForm);
574
+ this.formBuilder = inject(FormBuilder);
575
+ this.widgetConfigService = inject(WidgetConfigService);
576
+ this.controls = PRESET_NAME.DATA_POINTS_LIST;
577
+ this.minDecimalPlaces = MIN_DECIMAL_PLACES;
578
+ this.maxDecimalPlaces = MAX_DECIMAL_PLACES;
579
+ this.configForm = signal(undefined, ...(ngDevMode ? [{ debugName: "configForm" }] : []));
580
+ this.columnsFormArray = computed(() => this.configForm()?.get('columns'), ...(ngDevMode ? [{ debugName: "columnsFormArray" }] : []));
581
+ /**
582
+ * Debounced config for preview to prevent multiple series requests on initial load.
583
+ * Uses debounceTime to batch rapid emissions (e.g., from initConfig + GlobalContext processing).
584
+ */
585
+ this.previewConfig$ = this.widgetConfigService.currentConfig$.pipe(filter(config => !!config?.dateTimeContext), debounceTime(300));
586
+ }
587
+ ngOnInit() {
588
+ this.widgetConfigService.currentConfig$
589
+ .pipe(filter(config => !!config?.dateTimeContext), take(1), takeUntilDestroyed(this.destroyRef))
590
+ .subscribe(() => this.initForm());
591
+ }
592
+ ngAfterViewInit() {
593
+ this.widgetConfigService.setPreview(this.previewTemplate() ?? null);
594
+ }
595
+ onColumnDrop(event) {
596
+ const columnsArray = this.columnsFormArray();
597
+ const item = columnsArray.at(event.previousIndex);
598
+ columnsArray.removeAt(event.previousIndex);
599
+ columnsArray.insert(event.currentIndex, item);
600
+ columnsArray.controls.forEach((control, index) => control.patchValue({ order: index }));
601
+ }
602
+ initForm() {
603
+ const form = this.createForm();
604
+ this.form.form.addControl('config', form);
605
+ form.patchValue(this.config(), { emitEvent: false });
606
+ this.pushFormToService(form.value);
607
+ form.valueChanges
608
+ .pipe(distinctUntilChanged((prev, curr) => isEqual(prev, curr)), debounceTime(300), takeUntilDestroyed(this.destroyRef))
609
+ .subscribe(formValue => {
610
+ if (form.invalid) {
611
+ return;
612
+ }
613
+ const { decimalPlaces } = formValue;
614
+ if (typeof decimalPlaces === 'number' &&
615
+ (decimalPlaces < MIN_DECIMAL_PLACES || decimalPlaces > MAX_DECIMAL_PLACES)) {
616
+ return;
617
+ }
618
+ this.pushFormToService(formValue);
619
+ });
620
+ this.configForm.set(form);
621
+ }
622
+ pushFormToService(formValue) {
623
+ const formData = formValue;
624
+ const { columns, ...formValueWithoutColumns } = formData;
625
+ const currentOptions = this.widgetConfigService.currentConfig?.options;
626
+ this.widgetConfigService.updateConfig({
627
+ ...formValueWithoutColumns,
628
+ options: {
629
+ ...currentOptions,
630
+ columns: columns || []
631
+ }
632
+ });
633
+ }
634
+ createForm() {
635
+ const currentConfig = this.config();
636
+ const columns = this.migrateColumnsConfig(currentConfig);
637
+ return this.formBuilder.group({
638
+ decimalPlaces: [
639
+ currentConfig?.decimalPlaces ?? DEFAULT_DECIMAL_PLACES,
640
+ [
641
+ Validators.required,
642
+ Validators.min(MIN_DECIMAL_PLACES),
643
+ Validators.max(MAX_DECIMAL_PLACES),
644
+ Validators.pattern('^[0-9]+$')
645
+ ]
646
+ ],
647
+ columns: this.formBuilder.array(columns.map(col => this.createColumnFormGroup(col)), [Validators.required, Validators.minLength(1), this.minOneColumnVisible()])
648
+ });
649
+ }
650
+ createColumnFormGroup(column) {
651
+ return this.formBuilder.group({
652
+ id: [column.id, Validators.required],
653
+ label: [column.label, Validators.required],
654
+ visible: [column.visible],
655
+ order: [column.order]
656
+ });
657
+ }
658
+ migrateColumnsConfig(config) {
659
+ if (config?.options?.columns?.length > 0) {
660
+ return [...config.options.columns].sort((a, b) => a.order - b.order);
661
+ }
662
+ return this.defaultColumns.map(defaultCol => ({
663
+ ...defaultCol,
664
+ visible: config?.options?.[defaultCol.id] ?? defaultCol.visible
665
+ }));
666
+ }
667
+ minOneColumnVisible() {
668
+ return (control) => {
669
+ const columns = control.value;
670
+ if (!columns?.length) {
671
+ return null;
672
+ }
673
+ const visibleColumns = columns.filter(column => column.visible);
674
+ return visibleColumns.length >= 1 ? null : { atLeastOneColumnMustBeVisible: true };
675
+ };
676
+ }
677
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatapointsListConfigComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
678
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: DatapointsListConfigComponent, isStandalone: true, selector: "c8y-datapoints-list-view-config", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "previewTemplate", first: true, predicate: ["dataPointsListPreview"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (configForm(); as form) {\n <form\n class=\"no-card-context\"\n [formGroup]=\"form\"\n >\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Columns (drag to reorder)' | translate }}</legend>\n <c8y-list-group\n formArrayName=\"columns\"\n cdkDropList\n (cdkDropListDropped)=\"onColumnDrop($event)\"\n >\n @if (columnsFormArray().errors?.atLeastOneColumnMustBeVisible) {\n <div\n class=\"alert alert-warning m-t-8\"\n role=\"alert\"\n >\n {{ 'At least 1 column must be visible.' | translate }}\n </div>\n }\n\n @for (column of columnsFormArray().controls; track column.value.id; let i = $index) {\n <c8y-li\n class=\"c8y-list__item__collapse--container-small\"\n [formGroupName]=\"i\"\n cdkDrag\n >\n <c8y-li-drag-handle\n [title]=\"'Click and drag to reorder' | translate\"\n cdkDragHandle\n >\n <i c8yIcon=\"drag-reorder\"></i>\n </c8y-li-drag-handle>\n <c8y-li-checkbox\n class=\"a-s-center p-r-0\"\n [displayAsSwitch]=\"true\"\n formControlName=\"visible\"\n (click)=\"$event.stopPropagation()\"\n ></c8y-li-checkbox>\n <c8y-li-body>\n <div class=\"d-flex a-i-center\">\n <span class=\"text-truncate\">{{ column.value.label | translate }}</span>\n @switch (column.value.label) {\n @case ('Target') {\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help content' | translate\"\n [popover]=\"\n 'The Target column shows the value set on the target field of the data point'\n | translate\n \"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n }\n @case ('Diff') {\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help content' | translate\"\n [popover]=\"\n 'The Diff column shows the difference between the current value and the target value of the data point'\n | translate\n \"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n }\n }\n </div>\n </c8y-li-body>\n </c8y-li>\n }\n </c8y-list-group>\n </fieldset>\n\n <!-- decimal input -->\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 [min]=\"minDecimalPlaces\"\n [max]=\"maxDecimalPlaces\"\n />\n </c8y-form-group>\n </fieldset>\n </form>\n}\n\n<ng-template #dataPointsListPreview>\n @let previewConfig = previewConfig$ | async;\n @if (previewConfig && previewConfig.displayMode !== 'dashboard') {\n <c8y-local-controls\n [controls]=\"controls\"\n [displayMode]=\"previewConfig.displayMode!\"\n [config]=\"previewConfig\"\n [disabled]=\"true\"\n ></c8y-local-controls>\n }\n\n @if (previewConfig) {\n <c8y-datapoints-list\n [config]=\"previewConfig\"\n [isInPreviewMode]=\"true\"\n data-cy=\"c8y-datapoints-list-widget-config--preview-datapoints-list\"\n ></c8y-datapoints-list>\n }\n</ng-template>\n", dependencies: [{ kind: "component", type: DatapointsListViewComponent, selector: "c8y-datapoints-list", inputs: ["config", "isInPreviewMode"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1$1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1$1.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i1$1.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: DynamicComponentModule }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: ListGroupModule }, { kind: "component", type: i1.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i1.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i1.ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "component", type: i1.ListItemCheckboxComponent, selector: "c8y-list-item-checkbox, c8y-li-checkbox", inputs: ["selected", "indeterminate", "disabled", "displayAsSwitch"], outputs: ["onSelect"] }, { kind: "component", type: i1.ListItemDragHandleComponent, selector: "c8y-list-item-drag-handle, c8y-li-drag-handle" }, { kind: "component", type: LocalControlsComponent, selector: "c8y-local-controls", inputs: ["controls", "displayMode", "config", "isLoading", "disabled"], outputs: ["configChange", "refresh"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { 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: "directive", type: i3.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i3.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i4.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
679
+ }
680
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: DatapointsListConfigComponent, decorators: [{
681
+ type: Component,
682
+ args: [{ selector: 'c8y-datapoints-list-view-config', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
683
+ AsyncPipe,
684
+ C8yTranslatePipe,
685
+ DatapointsListViewComponent,
686
+ DragDropModule,
687
+ DynamicComponentModule,
688
+ FormGroupComponent,
689
+ IconDirective,
690
+ ListGroupModule,
691
+ LocalControlsComponent,
692
+ ReactiveFormsModule,
693
+ PopoverModule
694
+ ], template: "@if (configForm(); as form) {\n <form\n class=\"no-card-context\"\n [formGroup]=\"form\"\n >\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Columns (drag to reorder)' | translate }}</legend>\n <c8y-list-group\n formArrayName=\"columns\"\n cdkDropList\n (cdkDropListDropped)=\"onColumnDrop($event)\"\n >\n @if (columnsFormArray().errors?.atLeastOneColumnMustBeVisible) {\n <div\n class=\"alert alert-warning m-t-8\"\n role=\"alert\"\n >\n {{ 'At least 1 column must be visible.' | translate }}\n </div>\n }\n\n @for (column of columnsFormArray().controls; track column.value.id; let i = $index) {\n <c8y-li\n class=\"c8y-list__item__collapse--container-small\"\n [formGroupName]=\"i\"\n cdkDrag\n >\n <c8y-li-drag-handle\n [title]=\"'Click and drag to reorder' | translate\"\n cdkDragHandle\n >\n <i c8yIcon=\"drag-reorder\"></i>\n </c8y-li-drag-handle>\n <c8y-li-checkbox\n class=\"a-s-center p-r-0\"\n [displayAsSwitch]=\"true\"\n formControlName=\"visible\"\n (click)=\"$event.stopPropagation()\"\n ></c8y-li-checkbox>\n <c8y-li-body>\n <div class=\"d-flex a-i-center\">\n <span class=\"text-truncate\">{{ column.value.label | translate }}</span>\n @switch (column.value.label) {\n @case ('Target') {\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help content' | translate\"\n [popover]=\"\n 'The Target column shows the value set on the target field of the data point'\n | translate\n \"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n }\n @case ('Diff') {\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help content' | translate\"\n [popover]=\"\n 'The Diff column shows the difference between the current value and the target value of the data point'\n | translate\n \"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n }\n }\n </div>\n </c8y-li-body>\n </c8y-li>\n }\n </c8y-list-group>\n </fieldset>\n\n <!-- decimal input -->\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 [min]=\"minDecimalPlaces\"\n [max]=\"maxDecimalPlaces\"\n />\n </c8y-form-group>\n </fieldset>\n </form>\n}\n\n<ng-template #dataPointsListPreview>\n @let previewConfig = previewConfig$ | async;\n @if (previewConfig && previewConfig.displayMode !== 'dashboard') {\n <c8y-local-controls\n [controls]=\"controls\"\n [displayMode]=\"previewConfig.displayMode!\"\n [config]=\"previewConfig\"\n [disabled]=\"true\"\n ></c8y-local-controls>\n }\n\n @if (previewConfig) {\n <c8y-datapoints-list\n [config]=\"previewConfig\"\n [isInPreviewMode]=\"true\"\n data-cy=\"c8y-datapoints-list-widget-config--preview-datapoints-list\"\n ></c8y-datapoints-list>\n }\n</ng-template>\n" }]
695
+ }], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], previewTemplate: [{ type: i0.ViewChild, args: ['dataPointsListPreview', { isSignal: true }] }] } });
696
+
697
+ /**
698
+ * Generated bundle index. Do not edit.
699
+ */
700
+
701
+ export { DatapointsListConfigComponent, DatapointsListFetchService, DatapointsListService, DatapointsListViewComponent };
702
+ //# sourceMappingURL=c8y-ngx-components-widgets-implementations-datapoints-list.mjs.map