@c8y/ngx-components 1023.48.3 → 1023.52.0

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 (70) hide show
  1. package/asset-properties/index.d.ts +1 -2
  2. package/asset-properties/index.d.ts.map +1 -1
  3. package/context-dashboard/index.d.ts.map +1 -1
  4. package/datapoint-explorer/view/index.d.ts +8 -0
  5. package/datapoint-explorer/view/index.d.ts.map +1 -1
  6. package/datapoint-selector/index.d.ts +14 -1
  7. package/datapoint-selector/index.d.ts.map +1 -1
  8. package/echart/index.d.ts +7 -2
  9. package/echart/index.d.ts.map +1 -1
  10. package/echart/models/index.d.ts +1 -0
  11. package/echart/models/index.d.ts.map +1 -1
  12. package/fesm2022/c8y-ngx-components-asset-properties.mjs +3 -4
  13. package/fesm2022/c8y-ngx-components-asset-properties.mjs.map +1 -1
  14. package/fesm2022/c8y-ngx-components-context-dashboard.mjs +2 -3
  15. package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
  16. package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs +33 -4
  17. package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs.map +1 -1
  18. package/fesm2022/c8y-ngx-components-datapoint-selector.mjs +24 -7
  19. package/fesm2022/c8y-ngx-components-datapoint-selector.mjs.map +1 -1
  20. package/fesm2022/c8y-ngx-components-device-grid.mjs +1 -1
  21. package/fesm2022/c8y-ngx-components-device-grid.mjs.map +1 -1
  22. package/fesm2022/c8y-ngx-components-echart-models.mjs.map +1 -1
  23. package/fesm2022/c8y-ngx-components-echart.mjs +128 -52
  24. package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
  25. package/fesm2022/c8y-ngx-components-global-context.mjs +7 -2
  26. package/fesm2022/c8y-ngx-components-global-context.mjs.map +1 -1
  27. package/fesm2022/c8y-ngx-components-widgets-definitions-asset-table.mjs +127 -0
  28. package/fesm2022/c8y-ngx-components-widgets-definitions-asset-table.mjs.map +1 -0
  29. package/fesm2022/c8y-ngx-components-widgets-definitions-datapoints-graph.mjs +3 -3
  30. package/fesm2022/c8y-ngx-components-widgets-definitions-datapoints-graph.mjs.map +1 -1
  31. package/fesm2022/c8y-ngx-components-widgets-definitions.mjs +1 -0
  32. package/fesm2022/c8y-ngx-components-widgets-definitions.mjs.map +1 -1
  33. package/fesm2022/c8y-ngx-components-widgets-exports.mjs +8 -1
  34. package/fesm2022/c8y-ngx-components-widgets-exports.mjs.map +1 -1
  35. package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs +1959 -0
  36. package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs.map +1 -0
  37. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs +90 -56
  38. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs.map +1 -1
  39. package/fesm2022/c8y-ngx-components.mjs +1724 -1464
  40. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  41. package/global-context/index.d.ts +1 -0
  42. package/global-context/index.d.ts.map +1 -1
  43. package/index.d.ts +1453 -1339
  44. package/index.d.ts.map +1 -1
  45. package/locales/de.po +213 -3
  46. package/locales/es.po +213 -3
  47. package/locales/fr.po +213 -3
  48. package/locales/ja_JP.po +213 -3
  49. package/locales/ko.po +213 -3
  50. package/locales/locales.pot +219 -3
  51. package/locales/nl.po +213 -3
  52. package/locales/pl.po +213 -3
  53. package/locales/pt_BR.po +213 -3
  54. package/locales/zh_CN.po +213 -3
  55. package/locales/zh_TW.po +213 -3
  56. package/package.json +1 -1
  57. package/widgets/cockpit-exports/index.d.ts +6 -0
  58. package/widgets/cockpit-exports/index.d.ts.map +1 -1
  59. package/widgets/definitions/asset-table/index.d.ts +6 -0
  60. package/widgets/definitions/asset-table/index.d.ts.map +1 -0
  61. package/widgets/definitions/index.d.ts +1 -0
  62. package/widgets/definitions/index.d.ts.map +1 -1
  63. package/widgets/device-management-exports/index.d.ts +6 -0
  64. package/widgets/device-management-exports/index.d.ts.map +1 -1
  65. package/widgets/exports/index.d.ts +8 -1
  66. package/widgets/exports/index.d.ts.map +1 -1
  67. package/widgets/implementations/asset-table/index.d.ts +229 -0
  68. package/widgets/implementations/asset-table/index.d.ts.map +1 -0
  69. package/widgets/implementations/datapoints-graph/index.d.ts +14 -3
  70. package/widgets/implementations/datapoints-graph/index.d.ts.map +1 -1
@@ -0,0 +1,1959 @@
1
+ import { NgTemplateOutlet, AsyncPipe, CommonModule as CommonModule$1, NgClass } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { Component, runInInjectionContext, inject, computed, ChangeDetectionStrategy, Injectable, EventEmitter, ViewChild, Output, Input } from '@angular/core';
4
+ import * as i2$2 from '@angular/forms';
5
+ import { FormGroup, FormBuilder, FormControl, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
6
+ import * as i1$1 from '@c8y/client';
7
+ import { OperationService, InventoryService, QueriesUtil } from '@c8y/client';
8
+ import * as i1 from '@c8y/ngx-components';
9
+ import { DatePipe, BaseColumn, CustomColumn, IconDirective, AlertService, CellRendererContext, CommonModule, IntervalBasedReload, DEFAULT_INTERVAL_VALUES, WIDGET_TYPE_VALUES, CoreModule, CountdownIntervalComponent, ManagedObjectRealtimeService, WidgetGlobalAutoRefreshService, globalAutoRefreshLoading, ModalModule, SelectModule, DateTimePickerModule, C8yTranslateModule, DropdownFocusTrapDirective, EmptyStateComponent, ListGroupModule, C8yTranslatePipe, HumanizePipe } from '@c8y/ngx-components';
10
+ import { AssetSelectorModule } from '@c8y/ngx-components/assets-navigator';
11
+ import { WidgetConfigService, ContextDashboardService } from '@c8y/ngx-components/context-dashboard';
12
+ import { from, switchMap, isObservable, of, map, tap, BehaviorSubject, Subject, merge, startWith, scan, distinctUntilChanged, shareReplay, takeUntil, skip } from 'rxjs';
13
+ import { transform, identity, get, assign } from 'lodash';
14
+ import * as i2 from '@c8y/ngx-components/device-grid';
15
+ import { AlarmsDeviceGridColumn, StatusDeviceGridColumn } from '@c8y/ngx-components/device-grid';
16
+ import { gettext } from '@c8y/ngx-components/gettext';
17
+ import * as i2$1 from '@c8y/ngx-components/asset-properties';
18
+ import { AssetPropertyListComponent } from '@c8y/ngx-components/asset-properties';
19
+ import * as i1$2 from '@ngx-translate/core';
20
+ import { TranslateService } from '@ngx-translate/core';
21
+ import * as i3 from 'ngx-bootstrap/tooltip';
22
+ import { TooltipModule } from 'ngx-bootstrap/tooltip';
23
+ import { isEqual } from 'lodash-es';
24
+ import { RouterModule } from '@angular/router';
25
+ import { PopoverModule } from 'ngx-bootstrap/popover';
26
+ import { moveItemInArray, CdkDropList, CdkDrag } from '@angular/cdk/drag-drop';
27
+ import * as i3$2 from 'ngx-bootstrap/dropdown';
28
+ import { BsDropdownModule, BsDropdownDirective } from 'ngx-bootstrap/dropdown';
29
+ import * as i4 from '@ngx-formly/core';
30
+ import { FormlyModule } from '@ngx-formly/core';
31
+ import { BsModalService } from 'ngx-bootstrap/modal';
32
+ import * as i3$1 from '@c8y/ngx-components/icon-selector';
33
+ import { IconSelectorModule } from '@c8y/ngx-components/icon-selector';
34
+
35
+ class DateCellRendererComponent {
36
+ constructor(context, columnUtilService) {
37
+ this.context = context;
38
+ this.columnUtilService = columnUtilService;
39
+ this.isLink = false;
40
+ this.href = null;
41
+ }
42
+ ngOnInit() {
43
+ this.isLink = !!this.context?.property?.isLink;
44
+ this.href = this.isLink ? this.columnUtilService.getHref(this.context.item) : null;
45
+ }
46
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DateCellRendererComponent, deps: [{ token: i1.CellRendererContext }, { token: i2.ColumnUtilService }], target: i0.ɵɵFactoryTarget.Component }); }
47
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DateCellRendererComponent, isStandalone: true, selector: "c8y-date-cell-renderer", ngImport: i0, template: `
48
+ @if (isLink) {
49
+ <a [href]="href">
50
+ {{ context.value | c8yDate }}
51
+ </a>
52
+ } @else {
53
+ {{ context.value | c8yDate }}
54
+ }
55
+ `, isInline: true, dependencies: [{ kind: "pipe", type: DatePipe, name: "c8yDate" }] }); }
56
+ }
57
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DateCellRendererComponent, decorators: [{
58
+ type: Component,
59
+ args: [{
60
+ template: `
61
+ @if (isLink) {
62
+ <a [href]="href">
63
+ {{ context.value | c8yDate }}
64
+ </a>
65
+ } @else {
66
+ {{ context.value | c8yDate }}
67
+ }
68
+ `,
69
+ selector: 'c8y-date-cell-renderer',
70
+ imports: [DatePipe]
71
+ }]
72
+ }], ctorParameters: () => [{ type: i1.CellRendererContext }, { type: i2.ColumnUtilService }] });
73
+
74
+ const GLOBAL_INTERVAL_OPTION = 'global-interval';
75
+ const DEFAULT_INTERVAL_VALUE = 30_000;
76
+ const COMPARISON_OPTIONS = {
77
+ number: [
78
+ { label: gettext('Greater than'), value: 'GREATER_THAN', sign: '>' },
79
+ { label: gettext('Less than'), value: 'LESS_THAN', sign: '<' },
80
+ { label: gettext('Equal'), value: 'EQUAL', sign: '===' },
81
+ { label: gettext('Not equal'), value: 'NOT_EQUAL', sign: '!==' }
82
+ ],
83
+ date: [
84
+ { label: gettext('Before `date`'), value: 'BEFORE', sign: '<' },
85
+ { label: gettext('After `date`'), value: 'AFTER', sign: '>' },
86
+ { label: gettext('On `date`'), value: 'ON', sign: '===' }
87
+ ],
88
+ string: [
89
+ { label: gettext('Contains'), value: 'CONTAINS', sign: 'includes' },
90
+ { label: gettext('Equal'), value: 'EQUAL', sign: '===' },
91
+ { label: gettext('Not equal'), value: 'NOT_EQUAL', sign: '!==' },
92
+ { label: gettext('Starts with'), value: 'STARTS_WITH', sign: 'startsWith' },
93
+ { label: gettext('Ends with'), value: 'ENDS_WITH', sign: 'endsWith' }
94
+ ],
95
+ boolean: [
96
+ { label: gettext('Is true'), value: 'IS_TRUE', sign: 'true' },
97
+ { label: gettext('Is false'), value: 'IS_FALSE', sign: 'false' }
98
+ ]
99
+ };
100
+ class BaseColumnExtended extends BaseColumn {
101
+ }
102
+ class CustomColumnExtended extends CustomColumn {
103
+ }
104
+ class AlarmsDeviceGridColumnExtended extends AlarmsDeviceGridColumn {
105
+ constructor(initialColumnConfig) {
106
+ super(initialColumnConfig);
107
+ }
108
+ }
109
+
110
+ class DateAssetTableGridColumn extends BaseColumnExtended {
111
+ constructor(initialColumnConfig) {
112
+ super(initialColumnConfig);
113
+ this.path = this.path;
114
+ this.name = this.name;
115
+ this.header = this.header;
116
+ this.type = 'date';
117
+ this.cellRendererComponent = DateCellRendererComponent;
118
+ this.filterable = true;
119
+ this.filteringConfig = {
120
+ fields: [
121
+ {
122
+ fieldGroup: [
123
+ {
124
+ type: 'date-time',
125
+ key: 'after',
126
+ templateOptions: {
127
+ label: gettext('From date')
128
+ },
129
+ expressionProperties: {
130
+ 'templateOptions.maxDate': (model) => model?.before
131
+ }
132
+ },
133
+ {
134
+ type: 'date-time',
135
+ key: 'before',
136
+ templateOptions: {
137
+ label: gettext('To date')
138
+ },
139
+ expressionProperties: {
140
+ 'templateOptions.minDate': (model) => model?.after
141
+ }
142
+ }
143
+ ],
144
+ validators: {
145
+ atLeastOneFilled: {
146
+ expression: (formGroup) => {
147
+ const after = formGroup.get('after')?.value;
148
+ const before = formGroup.get('before')?.value;
149
+ return !!after || !!before;
150
+ },
151
+ message: gettext('Specify at least one date.')
152
+ }
153
+ }
154
+ }
155
+ ],
156
+ formGroup: new FormGroup({}),
157
+ getFilter: model => {
158
+ const filter = {};
159
+ const dates = model;
160
+ if (dates && (dates.after || dates.before)) {
161
+ filter.__and = [];
162
+ if (dates.after) {
163
+ const after = this.formatDate(dates.after);
164
+ filter.__and.push({
165
+ [`${this.path}.date`]: { __gt: after }
166
+ });
167
+ }
168
+ if (dates.before) {
169
+ const before = this.formatDate(dates.before);
170
+ filter.__and.push({
171
+ [`${this.path}.date`]: { __lt: before }
172
+ });
173
+ }
174
+ }
175
+ return filter;
176
+ }
177
+ };
178
+ this.sortable = true;
179
+ this.sortingConfig = {
180
+ pathSortingConfigs: [{ path: `${this.path}.date` }]
181
+ };
182
+ }
183
+ formatDate(dateToFormat) {
184
+ return new Date(dateToFormat).toISOString();
185
+ }
186
+ }
187
+
188
+ class IconCellRendererComponent {
189
+ constructor(context, computedPropertyService, columnUtilService) {
190
+ this.context = context;
191
+ this.computedPropertyService = computedPropertyService;
192
+ this.columnUtilService = columnUtilService;
193
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
194
+ this.computed$ = null;
195
+ }
196
+ async ngOnInit() {
197
+ if (this.context.property.computedConfig) {
198
+ this.computed$ = this.getComputedValue(this.context.property, this.context.item);
199
+ }
200
+ }
201
+ getComputedValue(property, context) {
202
+ return from(this.computedPropertyService.getByName(property.name)).pipe(switchMap(definition => {
203
+ let value = '-';
204
+ runInInjectionContext(definition.injector, () => {
205
+ if (property.computedConfig?.dp?.length > 0) {
206
+ property.computedConfig.dp[0].__target = { ...context };
207
+ }
208
+ value = definition.value({
209
+ config: property.computedConfig,
210
+ context,
211
+ metadata: { mode: 'singleValue' }
212
+ });
213
+ });
214
+ if (isObservable(value) || value instanceof Promise) {
215
+ return value;
216
+ }
217
+ else {
218
+ return of(value);
219
+ }
220
+ }));
221
+ }
222
+ matchedCondition(cellValue) {
223
+ const iconConfigs = this.context?.property?.iconConfig;
224
+ if (!Array.isArray(iconConfigs))
225
+ return null;
226
+ return (iconConfigs.find(config => {
227
+ const { comparison, value } = config;
228
+ if (!comparison || value === undefined || value === null)
229
+ return false;
230
+ return this.evaluateCondition(cellValue, comparison.value, value);
231
+ }) ?? null);
232
+ }
233
+ resolveValue(context) {
234
+ const { item, property } = context;
235
+ if (!item || !property) {
236
+ return undefined;
237
+ }
238
+ const path = property.path;
239
+ if (Array.isArray(path)) {
240
+ return path.reduce((acc, key) => acc?.[key], item);
241
+ }
242
+ return item[path];
243
+ }
244
+ evaluateCondition(cellValue, operator, value) {
245
+ switch (operator) {
246
+ case 'GREATER_THAN':
247
+ return cellValue > value;
248
+ case 'LESS_THAN':
249
+ return cellValue < value;
250
+ case 'EQUAL':
251
+ return cellValue === value;
252
+ case 'NOT_EQUAL':
253
+ return cellValue !== value;
254
+ case 'CONTAINS':
255
+ return typeof cellValue === 'string' && cellValue.includes(value);
256
+ case 'STARTS_WITH':
257
+ return typeof cellValue === 'string' && cellValue.startsWith(value);
258
+ case 'ENDS_WITH':
259
+ return typeof cellValue === 'string' && cellValue.endsWith(value);
260
+ case 'IS_TRUE':
261
+ return cellValue === true;
262
+ case 'IS_FALSE':
263
+ return cellValue === false;
264
+ case 'BEFORE':
265
+ return new Date(cellValue) < new Date(value);
266
+ case 'AFTER':
267
+ return new Date(cellValue) > new Date(value);
268
+ case 'ON':
269
+ return (new Date(cellValue).toDateString() === new Date(value).toDateString());
270
+ default:
271
+ return false;
272
+ }
273
+ }
274
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: IconCellRendererComponent, deps: [{ token: i1.CellRendererContext }, { token: i2$1.ComputedPropertiesService }, { token: i2.ColumnUtilService }], target: i0.ɵɵFactoryTarget.Component }); }
275
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: IconCellRendererComponent, isStandalone: true, selector: "c8y-icon-cell-renderer", ngImport: i0, template: ` <ng-template #iconAndValueTemplate let-value>
276
+ @if (matchedCondition(value); as match) {
277
+ <i class="m-r-4" [c8yIcon]="match.icon" [style.color]="match.color"></i>
278
+ @if (context.property.showIconAndValue) {
279
+ <span class="text-truncate" [title]="value">{{ value }}</span>
280
+ }
281
+ } @else {
282
+ <span class="text-truncate" [title]="value">{{ value }}</span>
283
+ }
284
+ </ng-template>
285
+
286
+ @if (context?.property?.isLink) {
287
+ <a class="d-flex a-i-center text-truncate" [href]="columnUtilService.getHref(context.item)">
288
+ @if (computed$ | async; as computed) {
289
+ <ng-container
290
+ *ngTemplateOutlet="iconAndValueTemplate; context: { $implicit: computed }"
291
+ ></ng-container>
292
+ } @else {
293
+ <ng-container
294
+ *ngTemplateOutlet="
295
+ iconAndValueTemplate;
296
+ context: {
297
+ $implicit: resolveValue(context)
298
+ }
299
+ "
300
+ ></ng-container>
301
+ }
302
+ </a>
303
+ } @else {
304
+ <div class="d-flex a-i-center text-truncate">
305
+ @if (computed$ | async; as computed) {
306
+ <ng-container
307
+ *ngTemplateOutlet="iconAndValueTemplate; context: { $implicit: computed }"
308
+ ></ng-container>
309
+ } @else {
310
+ <ng-container
311
+ *ngTemplateOutlet="
312
+ iconAndValueTemplate;
313
+ context: {
314
+ $implicit: resolveValue(context)
315
+ }
316
+ "
317
+ ></ng-container>
318
+ }
319
+ </div>
320
+ }`, isInline: true, dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
321
+ }
322
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: IconCellRendererComponent, decorators: [{
323
+ type: Component,
324
+ args: [{
325
+ template: ` <ng-template #iconAndValueTemplate let-value>
326
+ @if (matchedCondition(value); as match) {
327
+ <i class="m-r-4" [c8yIcon]="match.icon" [style.color]="match.color"></i>
328
+ @if (context.property.showIconAndValue) {
329
+ <span class="text-truncate" [title]="value">{{ value }}</span>
330
+ }
331
+ } @else {
332
+ <span class="text-truncate" [title]="value">{{ value }}</span>
333
+ }
334
+ </ng-template>
335
+
336
+ @if (context?.property?.isLink) {
337
+ <a class="d-flex a-i-center text-truncate" [href]="columnUtilService.getHref(context.item)">
338
+ @if (computed$ | async; as computed) {
339
+ <ng-container
340
+ *ngTemplateOutlet="iconAndValueTemplate; context: { $implicit: computed }"
341
+ ></ng-container>
342
+ } @else {
343
+ <ng-container
344
+ *ngTemplateOutlet="
345
+ iconAndValueTemplate;
346
+ context: {
347
+ $implicit: resolveValue(context)
348
+ }
349
+ "
350
+ ></ng-container>
351
+ }
352
+ </a>
353
+ } @else {
354
+ <div class="d-flex a-i-center text-truncate">
355
+ @if (computed$ | async; as computed) {
356
+ <ng-container
357
+ *ngTemplateOutlet="iconAndValueTemplate; context: { $implicit: computed }"
358
+ ></ng-container>
359
+ } @else {
360
+ <ng-container
361
+ *ngTemplateOutlet="
362
+ iconAndValueTemplate;
363
+ context: {
364
+ $implicit: resolveValue(context)
365
+ }
366
+ "
367
+ ></ng-container>
368
+ }
369
+ </div>
370
+ }`,
371
+ selector: 'c8y-icon-cell-renderer',
372
+ imports: [AsyncPipe, IconDirective, NgTemplateOutlet]
373
+ }]
374
+ }], ctorParameters: () => [{ type: i1.CellRendererContext }, { type: i2$1.ComputedPropertiesService }, { type: i2.ColumnUtilService }] });
375
+
376
+ class IconAssetTableGridColumn extends BaseColumnExtended {
377
+ constructor(initialColumnConfig) {
378
+ super(initialColumnConfig);
379
+ this.path = this.path;
380
+ this.name = this.name;
381
+ this.header = this.header;
382
+ this.computedConfig = this.computedConfig;
383
+ this.type = 'icon';
384
+ this.cellRendererComponent = IconCellRendererComponent;
385
+ this.iconConfig = this.iconConfig;
386
+ this.showIconAndValue = this.showIconAndValue;
387
+ this.filterable = false;
388
+ this.sortable = false;
389
+ }
390
+ }
391
+
392
+ class OperationCellRendererComponent {
393
+ constructor() {
394
+ this.operations = inject(OperationService);
395
+ this.alertService = inject(AlertService);
396
+ this.inventoryService = inject(InventoryService);
397
+ this.translateService = inject(TranslateService);
398
+ this.context = inject(CellRendererContext);
399
+ this.shouldShowButton = computed(() => {
400
+ const operationType = this.context.property?.operationType;
401
+ const device = this.context.item;
402
+ if (operationType !== 'maintenance') {
403
+ return true;
404
+ }
405
+ return this.canSwitchResponseInterval(device);
406
+ }, ...(ngDevMode ? [{ debugName: "shouldShowButton" }] : []));
407
+ }
408
+ async onOperationClick() {
409
+ const operationType = this.context.property?.operationType;
410
+ const device = this.context.item;
411
+ try {
412
+ if (operationType === 'maintenance') {
413
+ let payload;
414
+ if (this.canSwitchResponseInterval(device)) {
415
+ payload = {
416
+ id: device.id,
417
+ c8y_RequiredAvailability: {
418
+ responseInterval: -device.c8y_RequiredAvailability.responseInterval
419
+ }
420
+ };
421
+ }
422
+ else {
423
+ payload = { id: device.id, c8y_RequiredAvailability: null };
424
+ }
425
+ await this.inventoryService.update(payload);
426
+ this.alertService.success(gettext('Maintenance mode toggled.'));
427
+ }
428
+ else {
429
+ const deviceId = this.context.value;
430
+ const commandBody = typeof this.context.property.command === 'string'
431
+ ? JSON.parse(this.context.property.command)
432
+ : this.context.property.command;
433
+ const body = { deviceId, ...commandBody };
434
+ await this.operations.create(body);
435
+ this.alertService.success(gettext('Operation created.'));
436
+ }
437
+ }
438
+ catch (error) {
439
+ this.alertService.danger(this.translateService.instant(gettext('Failed to execute operation: "{{ errorMessage }}"'), {
440
+ errorMessage: this.translateService.instant(error.message)
441
+ }));
442
+ }
443
+ }
444
+ canSwitchResponseInterval(device) {
445
+ return (device &&
446
+ device.c8y_RequiredAvailability &&
447
+ device.c8y_RequiredAvailability.responseInterval &&
448
+ parseInt(device.c8y_RequiredAvailability.responseInterval, 10) !== 0);
449
+ }
450
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: OperationCellRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
451
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: OperationCellRendererComponent, isStandalone: true, selector: "c8y-operation-cell-renderer", ngImport: i0, template: `
452
+ @if (shouldShowButton()) {
453
+ <button class="btn btn-primary btn-sm" type="button" (click)="onOperationClick()">
454
+ {{ context.property?.name ?? '' | translate }}
455
+ </button>
456
+ } @else {
457
+ <span class="text-muted">-</span>
458
+ }
459
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
460
+ }
461
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: OperationCellRendererComponent, decorators: [{
462
+ type: Component,
463
+ args: [{
464
+ selector: 'c8y-operation-cell-renderer',
465
+ changeDetection: ChangeDetectionStrategy.OnPush,
466
+ template: `
467
+ @if (shouldShowButton()) {
468
+ <button class="btn btn-primary btn-sm" type="button" (click)="onOperationClick()">
469
+ {{ context.property?.name ?? '' | translate }}
470
+ </button>
471
+ } @else {
472
+ <span class="text-muted">-</span>
473
+ }
474
+ `,
475
+ imports: [CommonModule]
476
+ }]
477
+ }] });
478
+
479
+ class OperationAssetTableGridColumn extends BaseColumnExtended {
480
+ constructor(initialColumnConfig) {
481
+ super(initialColumnConfig);
482
+ this.path = this.path;
483
+ this.name = this.name;
484
+ this.header = this.header || gettext('Operation');
485
+ this.type = 'operation';
486
+ this.cellRendererComponent = OperationCellRendererComponent;
487
+ this.command = this.command;
488
+ this.isOperation = true;
489
+ this.operationType = this.operationType || 'operation';
490
+ this.filterable = false;
491
+ this.sortable = false;
492
+ }
493
+ }
494
+
495
+ class ComputedCellRendererComponent {
496
+ constructor(context, computedPropertyService) {
497
+ this.context = context;
498
+ this.computedPropertyService = computedPropertyService;
499
+ }
500
+ ngOnInit() {
501
+ this.computedValue = this.getCallbackComputedPropertyValue(this.context.property, this.context.item).pipe(map(value => (typeof value === 'object' && value !== null ? JSON.stringify(value) : value)));
502
+ }
503
+ getCallbackComputedPropertyValue(property, context) {
504
+ return from(this.computedPropertyService.getByName(property.name)).pipe(switchMap(definition => {
505
+ let value = '-';
506
+ runInInjectionContext(definition.injector, () => {
507
+ if (this.context?.property.computedConfig?.dp?.length > 0) {
508
+ this.context.property.computedConfig.dp[0].__target = { ...context };
509
+ }
510
+ value = definition.value({
511
+ config: this.context?.property.computedConfig,
512
+ context,
513
+ metadata: { mode: 'singleValue' }
514
+ });
515
+ });
516
+ if (isObservable(value) || value instanceof Promise) {
517
+ return value;
518
+ }
519
+ else {
520
+ return of(value);
521
+ }
522
+ }));
523
+ }
524
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ComputedCellRendererComponent, deps: [{ token: i1.CellRendererContext }, { token: i2$1.ComputedPropertiesService }], target: i0.ɵɵFactoryTarget.Component }); }
525
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: ComputedCellRendererComponent, isStandalone: true, selector: "c8y-computed-cell-renderer", ngImport: i0, template: `{{ computedValue | async }}`, isInline: true, dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }] }); }
526
+ }
527
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ComputedCellRendererComponent, decorators: [{
528
+ type: Component,
529
+ args: [{
530
+ template: `{{ computedValue | async }}`,
531
+ selector: 'c8y-computed-cell-renderer',
532
+ imports: [AsyncPipe]
533
+ }]
534
+ }], ctorParameters: () => [{ type: i1.CellRendererContext }, { type: i2$1.ComputedPropertiesService }] });
535
+
536
+ class ComputedAssetTableGridColumn extends BaseColumnExtended {
537
+ constructor(initialColumnConfig) {
538
+ super(initialColumnConfig);
539
+ this.name = this.name;
540
+ this.header = this.header;
541
+ this.computedConfig = this.computedConfig;
542
+ this.visible = this.visible;
543
+ this.type = 'computed';
544
+ this.cellRendererComponent = ComputedCellRendererComponent;
545
+ this.filterable = false;
546
+ this.sortable = true;
547
+ }
548
+ }
549
+
550
+ class DefaultCellRendererComponent {
551
+ constructor(context, columnUtilService) {
552
+ this.context = context;
553
+ this.columnUtilService = columnUtilService;
554
+ this.isLink = false;
555
+ this.href = null;
556
+ this.displayValue = null;
557
+ }
558
+ ngOnInit() {
559
+ this.isLink = !!this.context?.property?.isLink;
560
+ this.href = this.isLink ? this.columnUtilService.getHref(this.context.item) : null;
561
+ this.displayValue = this.formatValue(this.context.value);
562
+ }
563
+ formatValue(value) {
564
+ if (typeof value === 'object') {
565
+ return JSON.stringify(value);
566
+ }
567
+ return String(value);
568
+ }
569
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DefaultCellRendererComponent, deps: [{ token: i1.CellRendererContext }, { token: i2.ColumnUtilService }], target: i0.ɵɵFactoryTarget.Component }); }
570
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DefaultCellRendererComponent, isStandalone: true, selector: "c8y-text-cell-renderer", ngImport: i0, template: `
571
+ @if (isLink) {
572
+ <a [href]="href">{{ displayValue }}</a>
573
+ } @else {
574
+ {{ displayValue }}
575
+ }
576
+ `, isInline: true }); }
577
+ }
578
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DefaultCellRendererComponent, decorators: [{
579
+ type: Component,
580
+ args: [{
581
+ template: `
582
+ @if (isLink) {
583
+ <a [href]="href">{{ displayValue }}</a>
584
+ } @else {
585
+ {{ displayValue }}
586
+ }
587
+ `,
588
+ selector: 'c8y-text-cell-renderer'
589
+ }]
590
+ }], ctorParameters: () => [{ type: i1.CellRendererContext }, { type: i2.ColumnUtilService }] });
591
+
592
+ class DefaultAssetTableGridColumn extends CustomColumnExtended {
593
+ constructor(initialColumnConfig) {
594
+ super(initialColumnConfig);
595
+ this.type = this.type || 'default';
596
+ this.isLink = this.isLink || false;
597
+ this.cellRendererComponent = DefaultCellRendererComponent;
598
+ this.filterable = true;
599
+ this.sortable = true;
600
+ }
601
+ }
602
+
603
+ class AssetTableService {
604
+ constructor(inventoryService) {
605
+ this.inventoryService = inventoryService;
606
+ this.queriesUtil = new QueriesUtil();
607
+ }
608
+ getColumns(selectedProperties, operationColumns, config) {
609
+ const firstLinkableIndex = this.getFirstLinkableIndex(selectedProperties, config);
610
+ const propertyColumns = this.buildPropertyColumns(selectedProperties, firstLinkableIndex, config);
611
+ const opColumns = this.buildOperationColumns(operationColumns);
612
+ let allColumns = [...propertyColumns, ...opColumns];
613
+ if (config?.showStatusIcon) {
614
+ const statusCol = new StatusDeviceGridColumn();
615
+ statusCol.__origin = 'status';
616
+ statusCol.__id = 'status';
617
+ allColumns = [statusCol, ...allColumns];
618
+ }
619
+ return this.applySortingAndOrdering(allColumns, config);
620
+ }
621
+ buildAssetQueryObj(config) {
622
+ const assetFilter = config.device
623
+ ? [
624
+ {
625
+ __or: [
626
+ config.includeDescendants
627
+ ? { __isinhierarchyof: config.device.id }
628
+ : config.isDeviceAssetSelected
629
+ ? { id: config.device.id }
630
+ : { __bygroupid: config.device.id }
631
+ ]
632
+ }
633
+ ]
634
+ : [];
635
+ return {
636
+ __and: [
637
+ ...assetFilter,
638
+ {
639
+ __or: [
640
+ { __has: 'c8y_IsDevice' },
641
+ { __has: 'c8y_IsAsset' },
642
+ { __has: 'c8y_IsDeviceGroup' }
643
+ ]
644
+ }
645
+ ]
646
+ };
647
+ }
648
+ getAssets(config, columns, preConfiguredFilter = {}, pagination = { pageSize: 100 }) {
649
+ const queryObj = this.buildAssetQueryObj(config);
650
+ const columnQueryObj = this.getQueryObj(columns);
651
+ const preConfigFilterObj = this.extractFilters(preConfiguredFilter);
652
+ const filterQuery = this.buildAndFilterQuery(preConfigFilterObj);
653
+ const andConditions = [queryObj];
654
+ if (Object.keys(columnQueryObj.__filter).length > 0) {
655
+ andConditions.push({ __filter: columnQueryObj.__filter });
656
+ }
657
+ if (Object.keys(preConfigFilterObj).length > 0) {
658
+ andConditions.push({ __filter: filterQuery });
659
+ }
660
+ const combinedQueryObj = {
661
+ __filter: {
662
+ __and: andConditions
663
+ },
664
+ __orderby: columnQueryObj.__orderby
665
+ };
666
+ const query = this.queriesUtil.buildQuery(combinedQueryObj);
667
+ return this.inventoryService.list({
668
+ query,
669
+ pageSize: pagination.pageSize,
670
+ currentPage: pagination.currentPage || 1,
671
+ withTotalElements: true,
672
+ withTotalPages: true
673
+ });
674
+ }
675
+ migrateLegacyProperties(config) {
676
+ if (!config?.options?.properties) {
677
+ return config;
678
+ }
679
+ const selectedProperties = [];
680
+ const operationColumns = [];
681
+ for (const prop of config.options.properties) {
682
+ const isOperation = prop?.renderType === 'operationButton' || prop?.isAction;
683
+ if (isOperation) {
684
+ operationColumns.push({
685
+ header: prop.label,
686
+ operationType: prop.renderConfig?.actionType === 'toggleMaintenanceMode' ? 'maintenance' : 'operation',
687
+ operation: null,
688
+ command: JSON.stringify(prop?.config?.args?.[0] ?? {
689
+ description: 'Command description',
690
+ c8y_Command: { text: '<command>' }
691
+ }, null, 2),
692
+ buttonLabel: prop.renderConfig?.label ?? prop.label
693
+ });
694
+ continue;
695
+ }
696
+ // Map old renderConfig.map -> iconConfig using proper comparison
697
+ if (prop.renderConfig?.map &&
698
+ Array.isArray(prop.renderConfig.map) &&
699
+ prop.renderType === 'iconMap') {
700
+ prop.columnType = 'icon';
701
+ prop.name = prop.name || prop.keyPath?.[0] || prop.label;
702
+ prop.iconConfig = prop.renderConfig.map.map((mapItem) => {
703
+ const comparisonObj = this.lookupComparison(mapItem.comparison);
704
+ return {
705
+ comparison: comparisonObj,
706
+ color: mapItem.color,
707
+ icon: mapItem.icon,
708
+ value: mapItem.value
709
+ };
710
+ });
711
+ }
712
+ // preserve computed properties and build c8y_JsonSchema
713
+ if (prop.computed) {
714
+ prop.c8y_JsonSchema = this.convertIdToJsonSchema(prop);
715
+ const parts = prop.id.split('!!');
716
+ const propertyName = parts.length > 1 ? parts[1] : prop.id;
717
+ prop.name = propertyName;
718
+ }
719
+ prop.active = prop.__active;
720
+ // last keypath segment is used as name if name is not provided
721
+ prop.name = prop.name || prop.keyPath?.[prop.keyPath.length - 1] || prop.label;
722
+ selectedProperties.push(prop);
723
+ }
724
+ return {
725
+ ...config,
726
+ selectedProperties,
727
+ operationColumns,
728
+ refreshInterval: config.refreshInterval ?? 30000,
729
+ refreshOption: config.refreshOption ?? 'interval',
730
+ includeDescendants: config.includeDescendants ?? false,
731
+ isDeviceAssetSelected: config.isDeviceAssetSelected ?? true,
732
+ widgetInstanceGlobalAutoRefreshContext: config.widgetInstanceGlobalAutoRefreshContext ?? null,
733
+ widgetInstanceGlobalTimeContext: config.widgetInstanceGlobalTimeContext ?? false,
734
+ showAsLink: config.showAsLink ?? true,
735
+ showStatusIcon: config.showStatusIcon ?? true
736
+ };
737
+ }
738
+ migrateToLegacyProperties(config) {
739
+ if (config.options) {
740
+ delete config.options.properties;
741
+ }
742
+ const properties = [];
743
+ // Convert selectedProperties back to legacy format
744
+ for (const prop of config.selectedProperties || []) {
745
+ const legacyProp = { ...prop };
746
+ // Restore computed property fields
747
+ if (prop.computed) {
748
+ legacyProp.__active = prop.active;
749
+ legacyProp.id = prop.name ? `c8ySchema!!${prop.name}` : prop.id;
750
+ legacyProp.label = prop.header || prop.label || prop.name;
751
+ legacyProp.type = prop.c8y_JsonSchema?.properties?.[prop.name]?.type || 'string';
752
+ legacyProp.computed = true;
753
+ }
754
+ // Restore iconMap
755
+ if (prop.columnType === 'icon' && Array.isArray(prop.iconConfig)) {
756
+ legacyProp.renderType = 'iconMap';
757
+ legacyProp.renderConfig = {
758
+ map: prop.iconConfig.map((icon) => ({
759
+ comparison: icon.comparison?.value ?? icon.comparison,
760
+ color: icon.color,
761
+ icon: icon.icon,
762
+ value: icon.value
763
+ }))
764
+ };
765
+ }
766
+ // Restore alarm type
767
+ if (prop.columnType === 'alarm') {
768
+ legacyProp.renderType = 'alarm';
769
+ }
770
+ // Restore default type
771
+ if (!prop.columnType || prop.columnType === 'default' || prop.columnType === 'base') {
772
+ legacyProp.renderType = 'default';
773
+ }
774
+ legacyProp.id = 'c8ySchema!!' + prop.name;
775
+ legacyProp.__active = prop.active;
776
+ properties.push(legacyProp);
777
+ }
778
+ // Convert operationColumns back to legacy format
779
+ for (const op of config.operationColumns || []) {
780
+ let commandObj;
781
+ if (typeof op.command === 'string') {
782
+ try {
783
+ commandObj = JSON.parse(op.command);
784
+ }
785
+ catch {
786
+ commandObj = op.command; // fallback if not valid JSON
787
+ }
788
+ }
789
+ else {
790
+ commandObj = op.command;
791
+ }
792
+ properties.push({
793
+ label: op.header,
794
+ renderType: 'operationButton',
795
+ isAction: true,
796
+ renderConfig: {
797
+ args: Array.isArray(commandObj) ? commandObj : [commandObj],
798
+ label: op.buttonLabel,
799
+ actionType: op.operationType === 'maintenance' ? 'toggleMaintenanceMode' : 'createOperation',
800
+ deviceTypes: ['c8y_DeviceGroup', 'c8y_MQTTDevice', 'c8y_DeviceSubgroup']
801
+ },
802
+ keyPath: [op.operationType ? 'createOperation' : 'toggleMaintenanceMode'],
803
+ __active: true
804
+ });
805
+ }
806
+ // Build legacy config object
807
+ return {
808
+ ...config,
809
+ options: {
810
+ ...config.options,
811
+ properties
812
+ }
813
+ };
814
+ }
815
+ isMigrationNeeded(config) {
816
+ const legacyProps = config?.options?.properties;
817
+ if (!legacyProps?.length) {
818
+ return false;
819
+ }
820
+ if (!config.selectedProperties || !config.operationColumns) {
821
+ return true;
822
+ }
823
+ const selected = config.selectedProperties ?? [];
824
+ const operations = config.operationColumns ?? [];
825
+ if (legacyProps.length !== selected.length + operations.length) {
826
+ return true;
827
+ }
828
+ for (const legacyProp of legacyProps) {
829
+ if (legacyProp.renderType === 'operationButton') {
830
+ const legacyLabel = legacyProp.renderConfig?.label;
831
+ const match = operations.find(op => op.buttonLabel === legacyLabel);
832
+ if (!match) {
833
+ return true;
834
+ }
835
+ }
836
+ else {
837
+ const legacyName = legacyProp.name;
838
+ const match = selected.find(prop => prop.name === legacyName);
839
+ if (!match) {
840
+ return true;
841
+ }
842
+ }
843
+ }
844
+ return false;
845
+ }
846
+ getFirstLinkableIndex(selectedProperties, config) {
847
+ if (!selectedProperties?.length)
848
+ return -1;
849
+ if (config?.columnOrder?.length) {
850
+ for (const orderItem of config.columnOrder) {
851
+ if (orderItem.__origin !== 'selectedProperties')
852
+ continue;
853
+ const idx = selectedProperties.findIndex(prop => prop.name === orderItem.__id || prop.title === orderItem.__id);
854
+ if (idx === -1)
855
+ continue;
856
+ const type = this.getColumnType(selectedProperties[idx]);
857
+ if (type !== 'computed' && type !== 'alarm') {
858
+ return idx;
859
+ }
860
+ }
861
+ }
862
+ return selectedProperties.findIndex(prop => {
863
+ const type = this.getColumnType(prop);
864
+ return type !== 'computed' && type !== 'alarm';
865
+ });
866
+ }
867
+ getColumnType(prop) {
868
+ return prop.columnType
869
+ ? prop.columnType
870
+ : prop.name === 'c8y_ActiveAlarmsStatus'
871
+ ? 'alarm'
872
+ : prop.computed
873
+ ? 'computed'
874
+ : 'default';
875
+ }
876
+ buildPropertyColumns(selectedProperties, firstLinkableIndex, config) {
877
+ return (selectedProperties || []).map((prop, index) => {
878
+ const columnType = this.getColumnType(prop);
879
+ const isLink = index === firstLinkableIndex && config?.showAsLink !== undefined
880
+ ? config.showAsLink
881
+ : false;
882
+ const column = this.createPropertyColumn(prop, columnType, isLink, config);
883
+ column.__origin = 'selectedProperties';
884
+ column.__id = prop.name || prop.title;
885
+ return column;
886
+ });
887
+ }
888
+ createPropertyColumn(prop, columnType, isLink, config) {
889
+ switch (columnType) {
890
+ case 'alarm':
891
+ return new AlarmsDeviceGridColumnExtended({
892
+ name: prop.name || prop.title,
893
+ header: prop.label || prop.title,
894
+ visible: prop.visible ?? true
895
+ });
896
+ case 'date':
897
+ return new DateAssetTableGridColumn({
898
+ name: prop.name || prop.title,
899
+ header: prop.label || prop.title,
900
+ visible: prop.visible ?? true,
901
+ path: prop.name || prop.keyPath,
902
+ sortOrder: prop.sortOrder ?? null
903
+ });
904
+ case 'icon':
905
+ return new IconAssetTableGridColumn({
906
+ name: prop.name || prop.title,
907
+ path: prop.keyPath || prop.name,
908
+ header: prop.label || prop.title,
909
+ iconConfig: prop.iconConfig || {},
910
+ computedConfig: prop.computed ? prop.config || {} : null,
911
+ showIconAndValue: config?.showIconAndValue,
912
+ visible: prop.visible ?? true
913
+ });
914
+ case 'computed':
915
+ return new ComputedAssetTableGridColumn({
916
+ name: prop.name || prop.title,
917
+ header: prop.label || prop.title,
918
+ computedConfig: prop.config || {},
919
+ visible: prop.visible ?? true,
920
+ sortOrder: prop.sortOrder ?? null
921
+ });
922
+ case 'default':
923
+ default:
924
+ return new DefaultAssetTableGridColumn({
925
+ name: prop.name || prop.title,
926
+ header: prop.label || prop.title,
927
+ custom: true,
928
+ isLink,
929
+ type: 'default',
930
+ path: prop.keyPath || prop.name,
931
+ visible: prop.visible ?? true,
932
+ sortOrder: prop.sortOrder ?? null
933
+ });
934
+ }
935
+ }
936
+ buildOperationColumns(operationColumns) {
937
+ return (operationColumns || []).map(op => {
938
+ const col = new OperationAssetTableGridColumn({
939
+ name: op.buttonLabel,
940
+ header: op.header,
941
+ visible: op.visible ?? true,
942
+ operationType: op.operationType,
943
+ command: op.command,
944
+ path: 'id'
945
+ });
946
+ col.__origin = 'operationColumns';
947
+ col.__id = op.buttonLabel;
948
+ return col;
949
+ });
950
+ }
951
+ applySortingAndOrdering(allColumns, config) {
952
+ // Apply sort orders
953
+ if (config?.columnSortOrders) {
954
+ for (const [columnName, sortOrder] of Object.entries(config.columnSortOrders)) {
955
+ const col = allColumns.find(c => c.name === columnName);
956
+ if (col) {
957
+ col.sortOrder = sortOrder;
958
+ }
959
+ }
960
+ }
961
+ // Apply column order
962
+ if (!Array.isArray(config?.columnOrder) || config.columnOrder.length === 0) {
963
+ return allColumns;
964
+ }
965
+ const ordered = [];
966
+ for (const orderItem of config.columnOrder) {
967
+ const match = allColumns.find(c => c.__id === orderItem.__id &&
968
+ c.__origin === orderItem.__origin);
969
+ if (match) {
970
+ ordered.push(match);
971
+ }
972
+ }
973
+ const missing = allColumns.filter(c => !config.columnOrder.some(o => o.__id === c.__id &&
974
+ o.__origin === c.__origin));
975
+ return [...ordered, ...missing];
976
+ }
977
+ buildAndFilterQuery(filterObj) {
978
+ const andConditions = [];
979
+ Object.entries(filterObj).forEach(([key, value]) => {
980
+ if (Array.isArray(value)) {
981
+ value.forEach(v => andConditions.push({ [key]: v }));
982
+ }
983
+ else if (typeof value === 'object' && value !== null) {
984
+ // Convert { gt: '...' } → { __gt: '...' }
985
+ const opEntries = Object.entries(value).map(([opKey, opValue]) => {
986
+ const prefixedKey = opKey.startsWith('__') ? opKey : `__${opKey}`;
987
+ return [prefixedKey, opValue];
988
+ });
989
+ const opObj = Object.fromEntries(opEntries);
990
+ andConditions.push({ [key]: opObj });
991
+ }
992
+ else {
993
+ andConditions.push({ [key]: value });
994
+ }
995
+ });
996
+ return andConditions.length === 1 ? andConditions[0] : { __and: andConditions };
997
+ }
998
+ extractFilters(obj, parentKey, result = {}) {
999
+ Object.entries(obj).forEach(([key, value]) => {
1000
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
1001
+ const dateOps = ['after', 'before', 'gt', 'lt', 'ge', 'le'];
1002
+ const hasDateOp = Object.keys(value).some(op => dateOps.includes(op));
1003
+ if (hasDateOp) {
1004
+ // Automatically add ".date" for date filters
1005
+ const effectiveKey = parentKey ? `${parentKey}.date` : `${key}.date`;
1006
+ Object.entries(value).forEach(([opKey, opValue]) => {
1007
+ if (opValue != null && dateOps.includes(opKey)) {
1008
+ const op = opKey === 'after' ? 'gt' : opKey === 'before' ? 'lt' : opKey;
1009
+ result[effectiveKey] = {
1010
+ ...(result[effectiveKey] || {}),
1011
+ [op]: opValue
1012
+ };
1013
+ }
1014
+ });
1015
+ }
1016
+ else {
1017
+ const nextParent = parentKey ? `${parentKey}.${key}` : key;
1018
+ this.extractFilters(value, nextParent, result);
1019
+ }
1020
+ }
1021
+ else if (key === 'equals' && Array.isArray(value) && value.length > 0) {
1022
+ if (parentKey) {
1023
+ result[parentKey] = value.length === 1 ? value[0] : value;
1024
+ }
1025
+ }
1026
+ });
1027
+ return result;
1028
+ }
1029
+ /** Returns a query object defaultd on columns setup. */
1030
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1031
+ getQueryObj(columns, defaultFilter = {}) {
1032
+ return transform(columns, (query, column) => this.addColumnQuery(query, column), {
1033
+ __filter: {},
1034
+ __orderby: [],
1035
+ ...defaultFilter
1036
+ });
1037
+ }
1038
+ /** Extends given query with a part defaultd on the setup of given column. */
1039
+ addColumnQuery(query, column) {
1040
+ // when a column is marked as filterable
1041
+ if (column.filterable) {
1042
+ if (column.filterPredicate) {
1043
+ // so we use it as the expected value, * allow to search for it anywhere in the property
1044
+ query.__filter[column.path] = `*${column.filterPredicate}*`;
1045
+ }
1046
+ // in the case of custom filtering form, we're storing the query in `externalFilterQuery.query`
1047
+ if (column.externalFilterQuery) {
1048
+ const getFilter = column.filteringConfig.getFilter || identity;
1049
+ const queryObj = getFilter(column.externalFilterQuery);
1050
+ if (queryObj.__or) {
1051
+ query.__filter.__and = query.__filter.__and || [];
1052
+ query.__filter.__and.push(queryObj);
1053
+ }
1054
+ else if (queryObj.__and && get(query, '__filter.__and')) {
1055
+ queryObj.__and.map(obj => query.__filter.__and.push(obj));
1056
+ }
1057
+ else {
1058
+ assign(query.__filter, queryObj);
1059
+ }
1060
+ }
1061
+ }
1062
+ // when a column is sortable and has a specified sorting order
1063
+ if (column.sortable && column.sortOrder) {
1064
+ // add sorting condition for the configured column `path`
1065
+ query.__orderby.push({
1066
+ [column.path]: column.sortOrder === 'asc' ? 1 : -1
1067
+ });
1068
+ }
1069
+ return query;
1070
+ }
1071
+ /**
1072
+ * Lookup comparison object from COMPARISON_OPTIONS
1073
+ */
1074
+ lookupComparison(comparison) {
1075
+ const allOptions = Object.values(COMPARISON_OPTIONS).flat();
1076
+ return allOptions.find(o => o.value === comparison || o.sign === comparison) ?? allOptions[1];
1077
+ }
1078
+ /**
1079
+ * Convert id + label + type into a c8y_JsonSchema format
1080
+ * Example:
1081
+ * id: 'c8ySchema!!lastMeasurement'
1082
+ * label: 'Last measurement'
1083
+ * type: 'string'
1084
+ * =>
1085
+ * {
1086
+ * properties: {
1087
+ * lastMeasurement: {
1088
+ * label: 'Last measurement',
1089
+ * type: 'string'
1090
+ * }
1091
+ * }
1092
+ * }
1093
+ */
1094
+ convertIdToJsonSchema(legacyComputedProperty) {
1095
+ const { id, label, type } = legacyComputedProperty;
1096
+ const parts = id.split('!!');
1097
+ const propertyName = parts.length > 1 ? parts[1] : id;
1098
+ return {
1099
+ properties: {
1100
+ [propertyName]: {
1101
+ label: label || propertyName,
1102
+ type: type || 'string'
1103
+ }
1104
+ }
1105
+ };
1106
+ }
1107
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTableService, deps: [{ token: i1$1.InventoryService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1108
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTableService, providedIn: 'root' }); }
1109
+ }
1110
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTableService, decorators: [{
1111
+ type: Injectable,
1112
+ args: [{
1113
+ providedIn: 'root'
1114
+ }]
1115
+ }], ctorParameters: () => [{ type: i1$1.InventoryService }] });
1116
+
1117
+ class AssetTableAutoRefreshComponent extends IntervalBasedReload {
1118
+ constructor(cdRef, translateService, widgetGlobalAutoRefreshService) {
1119
+ super();
1120
+ this.cdRef = cdRef;
1121
+ this.translateService = translateService;
1122
+ this.widgetGlobalAutoRefreshService = widgetGlobalAutoRefreshService;
1123
+ /**
1124
+ * @inheritdoc
1125
+ */
1126
+ this.onCountdownEnded = new EventEmitter();
1127
+ /**
1128
+ * @inheritdoc
1129
+ */
1130
+ this.manuallyDisabledCountdown = false;
1131
+ this.GLOBAL_INTERVAL_OPTION = GLOBAL_INTERVAL_OPTION;
1132
+ this.REFRESH_INTERVAL_IN_MILLISECONDS = DEFAULT_INTERVAL_VALUES;
1133
+ this.WIDGET_TYPE_VALUES = WIDGET_TYPE_VALUES;
1134
+ this.isAutoRefreshDisabled = false;
1135
+ }
1136
+ ngOnInit() {
1137
+ this.isIntervalRefreshToggleOn =
1138
+ !this.isRefreshDisabled && (this.config.isAutoRefreshEnabled || !!this.refreshInterval);
1139
+ this.updateCountdownButtonTooltipText();
1140
+ }
1141
+ ngAfterViewInit() {
1142
+ if (this.isIntervalRefresh && this.refreshInterval) {
1143
+ this.startCountdown();
1144
+ }
1145
+ if (this.globalAutoRefreshEnabled) {
1146
+ this.countdownSubscription =
1147
+ this.widgetGlobalAutoRefreshService.countdownActions.countdownEnded$
1148
+ .pipe(tap(() => this.onCountdownEnded.emit()))
1149
+ .subscribe();
1150
+ }
1151
+ }
1152
+ ngOnChanges(changes) {
1153
+ const { isDisabled, config } = changes;
1154
+ if (config) {
1155
+ this.globalAutoRefreshEnabled = this.config.refreshOption === GLOBAL_INTERVAL_OPTION;
1156
+ }
1157
+ if (isDisabled) {
1158
+ this.isIntervalRefreshToggleOn =
1159
+ !this.isRefreshDisabled && (this.config.isAutoRefreshEnabled || !!this.refreshInterval);
1160
+ this.updateCountdownButtonTooltipText(gettext('Disabled'));
1161
+ }
1162
+ if (!this.isIntervalRefresh ||
1163
+ this.manuallyDisabledCountdown ||
1164
+ !this.config.isAutoRefreshEnabled) {
1165
+ return;
1166
+ }
1167
+ this.handleScrolling();
1168
+ }
1169
+ ngOnDestroy() {
1170
+ if (this.countdownSubscription) {
1171
+ this.countdownSubscription.unsubscribe();
1172
+ }
1173
+ }
1174
+ /**
1175
+ * @inheritdoc
1176
+ */
1177
+ countdownEnded() {
1178
+ /**
1179
+ * @inheritdoc
1180
+ */
1181
+ this.autoRefreshList();
1182
+ }
1183
+ reload() {
1184
+ if (this.isIntervalRefresh) {
1185
+ this.autoRefreshList();
1186
+ }
1187
+ else {
1188
+ this.onCountdownEnded.emit();
1189
+ }
1190
+ }
1191
+ /**
1192
+ * Stops the countdown and triggers a refresh action.
1193
+ * This function is responsible for halting the countdown interval component's operation.
1194
+ * After stopping the countdown, it emits an `onCountdownEnded` event.
1195
+ * This event is used to inform external components that the countdown has ended,
1196
+ * typically prompting them to reload or refresh their data.
1197
+ */
1198
+ autoRefreshList() {
1199
+ if (this.isIntervalRefreshToggleOn && this.config.isAutoRefreshEnabled) {
1200
+ this.countdownIntervalComponent.stop();
1201
+ }
1202
+ this.onCountdownEnded.emit();
1203
+ }
1204
+ /**
1205
+ * Enables and starts the countdown timer.
1206
+ *
1207
+ * This method makes the countdown visible (`hideCountdown` is set to false) and then
1208
+ * starts the countdown process. It ensures the countdown timer is updated immediately
1209
+ * by triggering change detection with `cdRef.detectChanges()` before starting the countdown.
1210
+ * This method encapsulates the logic required to initiate the countdown timer.
1211
+ */
1212
+ enableCountdown() {
1213
+ this.hideCountdown = false;
1214
+ // Prevents the countdown from getting stuck on an initial value.
1215
+ this.cdRef.detectChanges();
1216
+ this.startCountdown();
1217
+ }
1218
+ updateCountdownButtonTooltipText(customText) {
1219
+ if (customText) {
1220
+ this.toggleCountdownButtonTooltipText = customText;
1221
+ return;
1222
+ }
1223
+ this.toggleCountdownButtonTooltipText = this.isIntervalRefreshToggleOn
1224
+ ? this.translateService.instant(gettext('Disable auto refresh'))
1225
+ : this.translateService.instant(gettext('Enable auto refresh'));
1226
+ }
1227
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTableAutoRefreshComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$2.TranslateService }, { token: i1.WidgetGlobalAutoRefreshService }], target: i0.ɵɵFactoryTarget.Component }); }
1228
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AssetTableAutoRefreshComponent, isStandalone: true, selector: "c8y-asset-table-auto-refresh", inputs: { config: "config", isIntervalRefresh: "isIntervalRefresh", refreshInterval: "refreshInterval", isLoading: "isLoading", isScrolling: "isScrolling", isRefreshDisabled: "isRefreshDisabled" }, outputs: { onCountdownEnded: "onCountdownEnded" }, providers: [ManagedObjectRealtimeService], viewQueries: [{ propertyName: "countdownIntervalComponent", first: true, predicate: CountdownIntervalComponent, descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"m-l-auto d-flex p-t-4 p-b-4 p-r-4\">\n <div class=\"m-l-auto d-flex a-i-center\">\n @if (config.isAutoRefreshEnabled) {\n <label\n class=\"m-b-0 m-r-8 text-label-small text-truncate flex-no-shrink\"\n title=\" {{ 'Auto refresh' | translate }}\"\n >\n {{ 'Auto refresh' | translate }}\n </label>\n }\n <div class=\"input-group\">\n <label\n class=\"toggle-countdown\"\n [class.toggle-countdown-disabled]=\"isAutoRefreshDisabled\"\n [attr.aria-label]=\"toggleCountdownButtonTooltipText\"\n [tooltip]=\"toggleCountdownButtonTooltipText\"\n placement=\"bottom\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarms-widget--interval-toggle-button\"\n (click)=\"onToggleCountdownButtonState($event, WIDGET_TYPE_VALUES.ASSET_TABLE)\"\n />\n\n @if (isIntervalRefreshToggleOn) {\n <c8y-countdown-interval\n [countdownInterval]=\"refreshInterval\"\n (countdownEnded)=\"countdownEnded()\"\n ></c8y-countdown-interval>\n } @else {\n <i c8yIcon=\"pause\"></i>\n }\n </label>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule$1 }, { kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: i1.CountdownIntervalComponent, selector: "c8y-countdown-interval", inputs: ["countdownInterval", "config"], outputs: ["countdownEnded"] }, { kind: "ngmodule", type: AssetSelectorModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3.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"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }] }); }
1229
+ }
1230
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTableAutoRefreshComponent, decorators: [{
1231
+ type: Component,
1232
+ args: [{ selector: 'c8y-asset-table-auto-refresh', standalone: true, imports: [
1233
+ CommonModule$1,
1234
+ CoreModule,
1235
+ AssetSelectorModule,
1236
+ TooltipModule,
1237
+ CountdownIntervalComponent
1238
+ ], providers: [ManagedObjectRealtimeService], template: "<div class=\"m-l-auto d-flex p-t-4 p-b-4 p-r-4\">\n <div class=\"m-l-auto d-flex a-i-center\">\n @if (config.isAutoRefreshEnabled) {\n <label\n class=\"m-b-0 m-r-8 text-label-small text-truncate flex-no-shrink\"\n title=\" {{ 'Auto refresh' | translate }}\"\n >\n {{ 'Auto refresh' | translate }}\n </label>\n }\n <div class=\"input-group\">\n <label\n class=\"toggle-countdown\"\n [class.toggle-countdown-disabled]=\"isAutoRefreshDisabled\"\n [attr.aria-label]=\"toggleCountdownButtonTooltipText\"\n [tooltip]=\"toggleCountdownButtonTooltipText\"\n placement=\"bottom\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarms-widget--interval-toggle-button\"\n (click)=\"onToggleCountdownButtonState($event, WIDGET_TYPE_VALUES.ASSET_TABLE)\"\n />\n\n @if (isIntervalRefreshToggleOn) {\n <c8y-countdown-interval\n [countdownInterval]=\"refreshInterval\"\n (countdownEnded)=\"countdownEnded()\"\n ></c8y-countdown-interval>\n } @else {\n <i c8yIcon=\"pause\"></i>\n }\n </label>\n </div>\n </div>\n</div>\n" }]
1239
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$2.TranslateService }, { type: i1.WidgetGlobalAutoRefreshService }], propDecorators: { config: [{
1240
+ type: Input
1241
+ }], isIntervalRefresh: [{
1242
+ type: Input
1243
+ }], refreshInterval: [{
1244
+ type: Input
1245
+ }], isLoading: [{
1246
+ type: Input
1247
+ }], isScrolling: [{
1248
+ type: Input
1249
+ }], isRefreshDisabled: [{
1250
+ type: Input
1251
+ }], onCountdownEnded: [{
1252
+ type: Output
1253
+ }], countdownIntervalComponent: [{
1254
+ type: ViewChild,
1255
+ args: [CountdownIntervalComponent, { static: false }]
1256
+ }] } });
1257
+
1258
+ class AssetTableWidgetConfigComponent {
1259
+ set previewMapSet(template) {
1260
+ if (template) {
1261
+ this.widgetConfigService.setPreview(template);
1262
+ return;
1263
+ }
1264
+ this.widgetConfigService.setPreview(null);
1265
+ }
1266
+ constructor() {
1267
+ this.GLOBAL_INTERVAL_OPTION = GLOBAL_INTERVAL_OPTION;
1268
+ this.REFRESH_INTERVAL_IN_MILLISECONDS = DEFAULT_INTERVAL_VALUES;
1269
+ this.title = gettext('Asset Table');
1270
+ this.selectedAssets = [];
1271
+ this.refresh = new EventEmitter();
1272
+ /**
1273
+ * Current isLoading state. Based on it next countdown cycle is being started.
1274
+ */
1275
+ this.isLoading$ = new BehaviorSubject(false);
1276
+ this.destroy$ = new Subject();
1277
+ this.formBuilder = inject(FormBuilder);
1278
+ this.assetTableService = inject(AssetTableService);
1279
+ this.widgetGlobalAutoRefresh = inject(WidgetGlobalAutoRefreshService);
1280
+ this.alertService = inject(AlertService);
1281
+ this.widgetConfigService = inject(WidgetConfigService);
1282
+ this.contextDashboardService = inject(ContextDashboardService);
1283
+ this.columns = this.getColumns();
1284
+ this.queriesUtil = new QueriesUtil();
1285
+ this.serverSideDataCallback = this.onDataSourceModifier.bind(this);
1286
+ }
1287
+ ngOnInit() {
1288
+ this.initializeForm();
1289
+ this.setupGridHeaderFilterDependency();
1290
+ const mergedConfig$ = merge(this.widgetConfigService.currentConfig$, this.formGroup.valueChanges).pipe(startWith(this.formGroup.getRawValue()), scan((acc, value) => ({ ...acc, ...value }), {}), distinctUntilChanged((a, b) => isEqual(a, b)), shareReplay(1));
1291
+ mergedConfig$.pipe(takeUntil(this.destroy$)).subscribe(config => {
1292
+ if (!config.device && config.settings?.context) {
1293
+ config.device = { ...config.settings.context };
1294
+ }
1295
+ this.config = config;
1296
+ this.columns = this.getColumns(config);
1297
+ this.refreshOption = config?.refreshOption ?? GLOBAL_INTERVAL_OPTION;
1298
+ this.formGroup.patchValue(config, { emitEvent: false });
1299
+ if (config.device?.self) {
1300
+ config.isDeviceAssetSelected = 'c8y_IsDevice' in config.device;
1301
+ }
1302
+ this.updateFormDisabledState();
1303
+ if (config?.widgetInstanceGlobalAutoRefreshContext) {
1304
+ this.handleGlobalRefreshLoading();
1305
+ }
1306
+ });
1307
+ this.widgetConfigService.addOnBeforeSave(() => {
1308
+ const migratedConfig = this.assetTableService.migrateToLegacyProperties(this.config);
1309
+ this.widgetConfigService.updateConfig(migratedConfig, true);
1310
+ return true;
1311
+ });
1312
+ }
1313
+ ngOnDestroy() {
1314
+ this.destroy$.next();
1315
+ this.destroy$.complete();
1316
+ if (this.formGroup.controls.widgetInstanceGlobalAutoRefreshContext.value &&
1317
+ this.isLoading$.value) {
1318
+ this.widgetGlobalAutoRefresh.decrementLoading();
1319
+ }
1320
+ }
1321
+ /**
1322
+ * This method loads data when data grid requests it (e.g. on initial load or on column settings change).
1323
+ * It gets the object with current data grid setup and is supposed to return:
1324
+ * full response, list of items, paging object, the number of items in the filtered subset, the number of all items.
1325
+ */
1326
+ async onDataSourceModifier(dataSourceModifier) {
1327
+ const { res, data, paging } = await this.assetTableService.getAssets(this.config, dataSourceModifier.columns, this.formGroup.controls.filterPredicate.value ?? {});
1328
+ const serverSideDataResult = {
1329
+ res,
1330
+ data,
1331
+ paging,
1332
+ filteredSize: paging.totalElements,
1333
+ size: paging.totalElements
1334
+ };
1335
+ return serverSideDataResult;
1336
+ }
1337
+ async updateSelectedAssets() {
1338
+ try {
1339
+ this.isLoading$.next(true);
1340
+ this.refresh.emit();
1341
+ }
1342
+ catch (error) {
1343
+ this.alertService.addServerFailure(error);
1344
+ }
1345
+ finally {
1346
+ this.isLoading$.next(false);
1347
+ }
1348
+ }
1349
+ updateRefreshOption() {
1350
+ this.formGroup.controls.refreshOption.setValue(this.refreshOption);
1351
+ const isGlobalInterval = this.refreshOption === GLOBAL_INTERVAL_OPTION;
1352
+ this.formGroup.controls.widgetInstanceGlobalTimeContext.setValue(isGlobalInterval);
1353
+ this.updateConfigBasedOnRefreshOption();
1354
+ }
1355
+ getColumns(config) {
1356
+ if (!config) {
1357
+ return [];
1358
+ }
1359
+ const selectedProperties = config.selectedProperties || [];
1360
+ const operationColumns = config.operationColumns || [];
1361
+ return this.assetTableService.getColumns(selectedProperties, operationColumns, config);
1362
+ }
1363
+ handleGlobalRefreshLoading() {
1364
+ this.isLoading$
1365
+ .pipe(skip(1), globalAutoRefreshLoading(this.widgetGlobalAutoRefresh), takeUntil(this.destroy$))
1366
+ .subscribe();
1367
+ }
1368
+ updateConfigBasedOnRefreshOption() {
1369
+ const isInterval = this.refreshOption === 'interval';
1370
+ this.formGroup.controls.isAutoRefreshEnabled.setValue(isInterval);
1371
+ isInterval
1372
+ ? this.formGroup.get('refreshInterval').enable()
1373
+ : this.formGroup.get('refreshInterval').disable();
1374
+ }
1375
+ updateFormDisabledState() {
1376
+ const selectedProperties = this.formGroup.get('selectedProperties')?.value;
1377
+ const operationColumns = this.formGroup.get('operationColumns')?.value;
1378
+ const disable = (!selectedProperties || selectedProperties.length === 0) &&
1379
+ (!operationColumns || operationColumns.length === 0);
1380
+ this.contextDashboardService.formDisabled = disable;
1381
+ }
1382
+ setupGridHeaderFilterDependency() {
1383
+ const gridHeaderControl = this.formGroup.get('displayOptions.gridHeader');
1384
+ const filterControl = this.formGroup.get('displayOptions.filter');
1385
+ const configurableColumnsControl = this.formGroup.get('displayOptions.configurableColumnsEnabled');
1386
+ // Set initial state
1387
+ if (!gridHeaderControl?.value) {
1388
+ filterControl?.disable();
1389
+ configurableColumnsControl?.disable();
1390
+ }
1391
+ // Listen to changes
1392
+ gridHeaderControl?.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(isEnabled => {
1393
+ if (isEnabled) {
1394
+ filterControl?.enable();
1395
+ configurableColumnsControl?.enable();
1396
+ }
1397
+ else {
1398
+ filterControl?.disable();
1399
+ configurableColumnsControl?.disable();
1400
+ }
1401
+ });
1402
+ }
1403
+ initializeForm() {
1404
+ this.formGroup = this.createForm();
1405
+ }
1406
+ createForm() {
1407
+ return this.formBuilder.group({
1408
+ title: new FormControl('Asset table'),
1409
+ device: new FormControl(undefined),
1410
+ selectedProperties: [],
1411
+ operationColumns: [],
1412
+ isAutoRefreshEnabled: [true],
1413
+ refreshOption: GLOBAL_INTERVAL_OPTION,
1414
+ refreshInterval: new FormControl(DEFAULT_INTERVAL_VALUE),
1415
+ widgetInstanceGlobalAutoRefreshContext: [],
1416
+ widgetInstanceGlobalTimeContext: [],
1417
+ displayOptions: this.formBuilder.group({
1418
+ gridHeader: [true],
1419
+ bordered: [false],
1420
+ striped: [true],
1421
+ filter: [true],
1422
+ hover: [true],
1423
+ showLoadingIndicator: [true],
1424
+ configurableColumnsEnabled: [true]
1425
+ }),
1426
+ filterPredicate: [],
1427
+ showIconAndValue: [true],
1428
+ showAsLink: [false],
1429
+ includeDescendants: [true],
1430
+ showStatusIcon: [true]
1431
+ });
1432
+ }
1433
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTableWidgetConfigComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1434
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AssetTableWidgetConfigComponent, isStandalone: true, selector: "c8y-asset-table-widget-config", providers: [ManagedObjectRealtimeService], viewQueries: [{ propertyName: "previewMapSet", first: true, predicate: ["assetTablePreview"], descendants: true }], ngImport: i0, template: "<form [formGroup]=\"formGroup\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Date' | translate }}</legend>\n <div class=\"row tight-grid\">\n <c8y-form-group class=\"m-b-16 form-group-sm\">\n <div class=\"d-flex gap-8 a-i-center\">\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n title=\"{{ 'Refresh options`options for refreshing a view`' | translate }}\"\n [(ngModel)]=\"refreshOption\"\n [ngModelOptions]=\"{ standalone: true }\"\n (change)=\"updateRefreshOption()\"\n >\n <option\n [title]=\"'Refreshing after the given interval' | translate\"\n value=\"interval\"\n >\n {{ 'Use refresh interval' | translate }}\n </option>\n <option\n [title]=\"'Refreshing after the given interval, synchronized globally' | translate\"\n value=\"global-interval\"\n >\n {{ 'Use global refresh interval' | translate }}\n </option>\n </select>\n </div>\n @if (formGroup?.controls?.refreshOption?.value !== GLOBAL_INTERVAL_OPTION) {\n <div\n class=\"c8y-select-wrapper grow flex-no-shrink\"\n title=\" {{ 'Interval' | translate }}\"\n >\n <select\n class=\"form-control text-12\"\n [title]=\"'Refresh interval in seconds' | translate\"\n id=\"refreshInterval\"\n formControlName=\"refreshInterval\"\n data-cy=\"c8y-alarm-list-widget-config--interval-selector\"\n >\n @for (refreshInterval of REFRESH_INTERVAL_IN_MILLISECONDS; track refreshInterval) {\n <option [value]=\"refreshInterval\">\n {{ '{{ seconds }}s' | translate: { seconds: refreshInterval / 1000 } }}\n </option>\n }\n </select>\n </div>\n }\n </div>\n </c8y-form-group>\n </div>\n </fieldset>\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Display options' | translate }}</legend>\n <div formGroupName=\"displayOptions\">\n <!-- Data options -->\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Data' | translate }}</legend>\n <c8y-form-group\n class=\"m-b-8 form-group-sm d-flex-md flex-wrap gap-16\"\n [formGroup]=\"formGroup\"\n >\n <!-- hierarchical data -->\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show assets and all descendants' | translate\"\n >\n <input\n name=\"includeDescendants\"\n type=\"checkbox\"\n formControlName=\"includeDescendants\"\n />\n <span></span>\n <span translate>Include descendants</span>\n </label>\n <!-- Status icon -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"'Show status icon column' | translate\"\n >\n <input\n name=\"showStatusIcon\"\n type=\"checkbox\"\n formControlName=\"showStatusIcon\"\n />\n <span></span>\n <span translate>Show status icon column</span>\n </label>\n </c8y-form-group>\n </fieldset>\n <!-- Header options -->\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Header' | translate }}</legend>\n <c8y-form-group class=\"m-b-8 form-group-sm d-flex-md flex-wrap gap-16\">\n <!-- grid header -->\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Display the data grid header' | translate\"\n >\n <input\n name=\"gridHeader\"\n type=\"checkbox\"\n formControlName=\"gridHeader\"\n />\n <span></span>\n <span translate>Table header</span>\n </label>\n <!-- show filter label -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"'Display applied filters in the header' | translate\"\n >\n <input\n name=\"filter\"\n type=\"checkbox\"\n formControlName=\"filter\"\n />\n <span></span>\n <span translate>Active filters</span>\n </label>\n <!-- enable configurable columns -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"'Display columns configuration button' | translate\"\n >\n <input\n name=\"configurableColumnsEnabled\"\n type=\"checkbox\"\n formControlName=\"configurableColumnsEnabled\"\n />\n <span></span>\n <span translate>Configure columns</span>\n </label>\n </c8y-form-group>\n </fieldset>\n\n <!-- Row options -->\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Row' | translate }}</legend>\n <c8y-form-group class=\"m-b-8 d-flex-md gap-16 flex-wrap form-group-sm\">\n <!-- zebra stripes -->\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Alternate row background for readability' | translate\"\n >\n <input\n name=\"striped\"\n type=\"checkbox\"\n formControlName=\"striped\"\n />\n <span></span>\n <span translate>Striped rows</span>\n </label>\n <!-- hover effect -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"'Change row background color on hover' | translate\"\n >\n <input\n name=\"hover\"\n type=\"checkbox\"\n formControlName=\"hover\"\n />\n <span></span>\n <span translate>Hover highlight</span>\n </label>\n <!-- show loading indicator -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"'Show a spinner while data loads' | translate\"\n >\n <input\n name=\"showLoadingIndicator\"\n type=\"checkbox\"\n formControlName=\"showLoadingIndicator\"\n />\n <span></span>\n <span translate>Loading indicator</span>\n </label>\n </c8y-form-group>\n </fieldset>\n\n <!-- Cell options -->\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Cell' | translate }}</legend>\n <div class=\"d-flex-md gap-16 flex-wrap m-b-8\">\n <!-- cell borders (inside displayOptions) -->\n <c8y-form-group class=\"m-b-0 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Draw borders around table cells' | translate\"\n >\n <input\n name=\"bordered\"\n type=\"checkbox\"\n formControlName=\"bordered\"\n />\n <span></span>\n <span translate>Cell borders</span>\n </label>\n </c8y-form-group>\n <!-- Cell content options (outside displayOptions formGroup, at root form level) -->\n <c8y-form-group\n class=\"m-b-0 form-group-sm d-flex-md flex-wrap gap-16\"\n [formGroup]=\"formGroup\"\n >\n <!-- show icon and value -->\n <label\n class=\"c8y-checkbox\"\n [title]=\"'When cell rendering is set to icon, show both icon and value' | translate\"\n >\n <input\n name=\"showIconAndValue\"\n type=\"checkbox\"\n formControlName=\"showIconAndValue\"\n />\n <span></span>\n <span translate>Icon with value</span>\n </label>\n <!-- show as link -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"\n 'Render the first column (excluding computed and alarm types) as a link to the asset details.'\n | translate\n \"\n >\n <input\n name=\"showAsLink\"\n type=\"checkbox\"\n formControlName=\"showAsLink\"\n />\n <span></span>\n <span translate>First column link</span>\n </label>\n </c8y-form-group>\n </div>\n </fieldset>\n </div>\n </fieldset>\n</form>\n\n<ng-template #assetTablePreview>\n @if (\n !formGroup ||\n columns?.length === 0 ||\n ((config.selectedProperties?.length === 0 || config.selectedProperties === null) &&\n (config.operationColumns?.length === 0 || config.operationColumns === null))\n ) {\n <c8y-ui-empty-state\n class=\"d-block m-t-24\"\n [icon]=\"'search'\"\n [title]=\"'No asset table' | translate\"\n [subtitle]=\"'Add property' | translate\"\n ></c8y-ui-empty-state>\n } @else {\n @if (!formGroup.controls.widgetInstanceGlobalTimeContext.value) {\n <c8y-asset-table-auto-refresh\n class=\"d-contents\"\n [isIntervalRefresh]=\"!!formGroup.controls.refreshInterval.value\"\n [refreshInterval]=\"formGroup.controls.refreshInterval.value\"\n [config]=\"formGroup.value\"\n [isRefreshDisabled]=\"false\"\n [isLoading]=\"isLoading$\"\n (onCountdownEnded)=\"updateSelectedAssets()\"\n ></c8y-asset-table-auto-refresh>\n }\n <c8y-data-grid\n class=\"content-fullpage d-flex d-col border-top border-bottom\"\n [title]=\"title\"\n [displayOptions]=\"formGroup.controls.displayOptions.value\"\n [columns]=\"columns\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [configureColumnsEnabled]=\"formGroup.get('displayOptions.configurableColumnsEnabled')?.value\"\n [refresh]=\"refresh\"\n >\n <c8y-ui-empty-state\n [icon]=\"'search'\"\n [title]=\"'No results to display.' | translate\"\n [subtitle]=\"'Refine your search terms or check your spelling.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </c8y-data-grid>\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule$1 }, { kind: "ngmodule", type: CoreModule }, { kind: "component", type: i1.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: i1.DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "loading", "columns", "rows", "pagination", "childNodePagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows", "treeGrid", "hideReload", "childNodesProperty", "parentNodeLabelProperty"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "directive", type: i2$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i2$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2$2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "ngmodule", type: AssetSelectorModule }, { kind: "component", type: AssetTableAutoRefreshComponent, selector: "c8y-asset-table-auto-refresh", inputs: ["config", "isIntervalRefresh", "refreshInterval", "isLoading", "isScrolling", "isRefreshDisabled"], outputs: ["onCountdownEnded"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }] }); }
1435
+ }
1436
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTableWidgetConfigComponent, decorators: [{
1437
+ type: Component,
1438
+ args: [{ selector: 'c8y-asset-table-widget-config', standalone: true, imports: [
1439
+ CommonModule$1,
1440
+ CoreModule,
1441
+ AssetSelectorModule,
1442
+ AssetTableAutoRefreshComponent,
1443
+ TooltipModule
1444
+ ], providers: [ManagedObjectRealtimeService], template: "<form [formGroup]=\"formGroup\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Date' | translate }}</legend>\n <div class=\"row tight-grid\">\n <c8y-form-group class=\"m-b-16 form-group-sm\">\n <div class=\"d-flex gap-8 a-i-center\">\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n title=\"{{ 'Refresh options`options for refreshing a view`' | translate }}\"\n [(ngModel)]=\"refreshOption\"\n [ngModelOptions]=\"{ standalone: true }\"\n (change)=\"updateRefreshOption()\"\n >\n <option\n [title]=\"'Refreshing after the given interval' | translate\"\n value=\"interval\"\n >\n {{ 'Use refresh interval' | translate }}\n </option>\n <option\n [title]=\"'Refreshing after the given interval, synchronized globally' | translate\"\n value=\"global-interval\"\n >\n {{ 'Use global refresh interval' | translate }}\n </option>\n </select>\n </div>\n @if (formGroup?.controls?.refreshOption?.value !== GLOBAL_INTERVAL_OPTION) {\n <div\n class=\"c8y-select-wrapper grow flex-no-shrink\"\n title=\" {{ 'Interval' | translate }}\"\n >\n <select\n class=\"form-control text-12\"\n [title]=\"'Refresh interval in seconds' | translate\"\n id=\"refreshInterval\"\n formControlName=\"refreshInterval\"\n data-cy=\"c8y-alarm-list-widget-config--interval-selector\"\n >\n @for (refreshInterval of REFRESH_INTERVAL_IN_MILLISECONDS; track refreshInterval) {\n <option [value]=\"refreshInterval\">\n {{ '{{ seconds }}s' | translate: { seconds: refreshInterval / 1000 } }}\n </option>\n }\n </select>\n </div>\n }\n </div>\n </c8y-form-group>\n </div>\n </fieldset>\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Display options' | translate }}</legend>\n <div formGroupName=\"displayOptions\">\n <!-- Data options -->\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Data' | translate }}</legend>\n <c8y-form-group\n class=\"m-b-8 form-group-sm d-flex-md flex-wrap gap-16\"\n [formGroup]=\"formGroup\"\n >\n <!-- hierarchical data -->\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show assets and all descendants' | translate\"\n >\n <input\n name=\"includeDescendants\"\n type=\"checkbox\"\n formControlName=\"includeDescendants\"\n />\n <span></span>\n <span translate>Include descendants</span>\n </label>\n <!-- Status icon -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"'Show status icon column' | translate\"\n >\n <input\n name=\"showStatusIcon\"\n type=\"checkbox\"\n formControlName=\"showStatusIcon\"\n />\n <span></span>\n <span translate>Show status icon column</span>\n </label>\n </c8y-form-group>\n </fieldset>\n <!-- Header options -->\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Header' | translate }}</legend>\n <c8y-form-group class=\"m-b-8 form-group-sm d-flex-md flex-wrap gap-16\">\n <!-- grid header -->\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Display the data grid header' | translate\"\n >\n <input\n name=\"gridHeader\"\n type=\"checkbox\"\n formControlName=\"gridHeader\"\n />\n <span></span>\n <span translate>Table header</span>\n </label>\n <!-- show filter label -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"'Display applied filters in the header' | translate\"\n >\n <input\n name=\"filter\"\n type=\"checkbox\"\n formControlName=\"filter\"\n />\n <span></span>\n <span translate>Active filters</span>\n </label>\n <!-- enable configurable columns -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"'Display columns configuration button' | translate\"\n >\n <input\n name=\"configurableColumnsEnabled\"\n type=\"checkbox\"\n formControlName=\"configurableColumnsEnabled\"\n />\n <span></span>\n <span translate>Configure columns</span>\n </label>\n </c8y-form-group>\n </fieldset>\n\n <!-- Row options -->\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Row' | translate }}</legend>\n <c8y-form-group class=\"m-b-8 d-flex-md gap-16 flex-wrap form-group-sm\">\n <!-- zebra stripes -->\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Alternate row background for readability' | translate\"\n >\n <input\n name=\"striped\"\n type=\"checkbox\"\n formControlName=\"striped\"\n />\n <span></span>\n <span translate>Striped rows</span>\n </label>\n <!-- hover effect -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"'Change row background color on hover' | translate\"\n >\n <input\n name=\"hover\"\n type=\"checkbox\"\n formControlName=\"hover\"\n />\n <span></span>\n <span translate>Hover highlight</span>\n </label>\n <!-- show loading indicator -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"'Show a spinner while data loads' | translate\"\n >\n <input\n name=\"showLoadingIndicator\"\n type=\"checkbox\"\n formControlName=\"showLoadingIndicator\"\n />\n <span></span>\n <span translate>Loading indicator</span>\n </label>\n </c8y-form-group>\n </fieldset>\n\n <!-- Cell options -->\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Cell' | translate }}</legend>\n <div class=\"d-flex-md gap-16 flex-wrap m-b-8\">\n <!-- cell borders (inside displayOptions) -->\n <c8y-form-group class=\"m-b-0 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Draw borders around table cells' | translate\"\n >\n <input\n name=\"bordered\"\n type=\"checkbox\"\n formControlName=\"bordered\"\n />\n <span></span>\n <span translate>Cell borders</span>\n </label>\n </c8y-form-group>\n <!-- Cell content options (outside displayOptions formGroup, at root form level) -->\n <c8y-form-group\n class=\"m-b-0 form-group-sm d-flex-md flex-wrap gap-16\"\n [formGroup]=\"formGroup\"\n >\n <!-- show icon and value -->\n <label\n class=\"c8y-checkbox\"\n [title]=\"'When cell rendering is set to icon, show both icon and value' | translate\"\n >\n <input\n name=\"showIconAndValue\"\n type=\"checkbox\"\n formControlName=\"showIconAndValue\"\n />\n <span></span>\n <span translate>Icon with value</span>\n </label>\n <!-- show as link -->\n <label\n class=\"c8y-checkbox m-t-0\"\n [title]=\"\n 'Render the first column (excluding computed and alarm types) as a link to the asset details.'\n | translate\n \"\n >\n <input\n name=\"showAsLink\"\n type=\"checkbox\"\n formControlName=\"showAsLink\"\n />\n <span></span>\n <span translate>First column link</span>\n </label>\n </c8y-form-group>\n </div>\n </fieldset>\n </div>\n </fieldset>\n</form>\n\n<ng-template #assetTablePreview>\n @if (\n !formGroup ||\n columns?.length === 0 ||\n ((config.selectedProperties?.length === 0 || config.selectedProperties === null) &&\n (config.operationColumns?.length === 0 || config.operationColumns === null))\n ) {\n <c8y-ui-empty-state\n class=\"d-block m-t-24\"\n [icon]=\"'search'\"\n [title]=\"'No asset table' | translate\"\n [subtitle]=\"'Add property' | translate\"\n ></c8y-ui-empty-state>\n } @else {\n @if (!formGroup.controls.widgetInstanceGlobalTimeContext.value) {\n <c8y-asset-table-auto-refresh\n class=\"d-contents\"\n [isIntervalRefresh]=\"!!formGroup.controls.refreshInterval.value\"\n [refreshInterval]=\"formGroup.controls.refreshInterval.value\"\n [config]=\"formGroup.value\"\n [isRefreshDisabled]=\"false\"\n [isLoading]=\"isLoading$\"\n (onCountdownEnded)=\"updateSelectedAssets()\"\n ></c8y-asset-table-auto-refresh>\n }\n <c8y-data-grid\n class=\"content-fullpage d-flex d-col border-top border-bottom\"\n [title]=\"title\"\n [displayOptions]=\"formGroup.controls.displayOptions.value\"\n [columns]=\"columns\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [configureColumnsEnabled]=\"formGroup.get('displayOptions.configurableColumnsEnabled')?.value\"\n [refresh]=\"refresh\"\n >\n <c8y-ui-empty-state\n [icon]=\"'search'\"\n [title]=\"'No results to display.' | translate\"\n [subtitle]=\"'Refine your search terms or check your spelling.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </c8y-data-grid>\n }\n</ng-template>\n" }]
1445
+ }], ctorParameters: () => [], propDecorators: { previewMapSet: [{
1446
+ type: ViewChild,
1447
+ args: ['assetTablePreview']
1448
+ }] } });
1449
+
1450
+ class AssetTableWidgetViewComponent {
1451
+ constructor() {
1452
+ /**
1453
+ * Current isLoading state. Based on it next countdown cycle is being started.
1454
+ */
1455
+ this.isLoading$ = new BehaviorSubject(false);
1456
+ this.selectedAssets = [];
1457
+ this.columns = [];
1458
+ this.refresh = new EventEmitter();
1459
+ this.pagination = {
1460
+ pageSize: 100,
1461
+ currentPage: 1
1462
+ };
1463
+ this.destroy$ = new Subject();
1464
+ this.widgetGlobalAutoRefresh = inject(WidgetGlobalAutoRefreshService);
1465
+ this.assetTableService = inject(AssetTableService);
1466
+ this.alertService = inject(AlertService);
1467
+ this.serverSideDataCallback = this.onDataSourceModifier.bind(this);
1468
+ }
1469
+ ngOnInit() {
1470
+ if (this.assetTableService.isMigrationNeeded(this.config)) {
1471
+ this.config = this.assetTableService.migrateLegacyProperties(this.config);
1472
+ }
1473
+ this.columns = this.assetTableService.getColumns(this.config?.selectedProperties, this.config?.operationColumns, this.config);
1474
+ if (this.config.widgetInstanceGlobalAutoRefreshContext) {
1475
+ this.handleGlobalRefreshLoading();
1476
+ }
1477
+ }
1478
+ ngOnDestroy() {
1479
+ this.destroy$.next();
1480
+ this.destroy$.complete();
1481
+ if (this.config?.widgetInstanceGlobalAutoRefreshContext && this.isLoading$.value) {
1482
+ this.widgetGlobalAutoRefresh.decrementLoading();
1483
+ }
1484
+ }
1485
+ /**
1486
+ * This method loads data when data grid requests it (e.g. on initial load or on column settings change).
1487
+ * It gets the object with current data grid setup and is supposed to return:
1488
+ * full response, list of items, paging object, the number of items in the filtered subset, the number of all items.
1489
+ */
1490
+ async onDataSourceModifier(dataSourceModifier) {
1491
+ const { res, data, paging } = await this.assetTableService.getAssets(this.config, dataSourceModifier.columns, this.config.filterPredicate ?? {}, dataSourceModifier.pagination);
1492
+ const serverSideDataResult = {
1493
+ res,
1494
+ data,
1495
+ paging,
1496
+ filteredSize: paging.totalElements,
1497
+ size: paging.totalElements
1498
+ };
1499
+ return serverSideDataResult;
1500
+ }
1501
+ updateSelectedAssets() {
1502
+ try {
1503
+ this.isLoading$.next(true);
1504
+ this.refresh.emit();
1505
+ }
1506
+ catch (error) {
1507
+ this.alertService.addServerFailure(error);
1508
+ }
1509
+ finally {
1510
+ this.isLoading$.next(false);
1511
+ }
1512
+ }
1513
+ handleGlobalRefreshLoading() {
1514
+ this.isLoading$
1515
+ .pipe(skip(1), globalAutoRefreshLoading(this.widgetGlobalAutoRefresh), takeUntil(this.destroy$))
1516
+ .subscribe();
1517
+ }
1518
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTableWidgetViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1519
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AssetTableWidgetViewComponent, isStandalone: true, selector: "c8y-asset-table-widget-view", inputs: { config: "config" }, ngImport: i0, template: "@if (!config.widgetInstanceGlobalTimeContext) {\n <c8y-asset-table-auto-refresh\n class=\"d-contents\"\n [isIntervalRefresh]=\"!!config.refreshInterval\"\n [refreshInterval]=\"config.refreshInterval\"\n [config]=\"config\"\n [isRefreshDisabled]=\"false\"\n [isLoading]=\"isLoading$\"\n (onCountdownEnded)=\"updateSelectedAssets()\"\n ></c8y-asset-table-auto-refresh>\n}\n<c8y-data-grid\n class=\"content-fullpage d-flex d-col border-top border-bottom\"\n [title]=\"config.title\"\n [displayOptions]=\"config.displayOptions\"\n [columns]=\"columns\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [pagination]=\"pagination\"\n (onPageSizeChange)=\"pagination = $event.pagination\"\n [refresh]=\"refresh\"\n>\n <c8y-ui-empty-state\n [icon]=\"'search'\"\n [title]=\"'No results to display.' | translate\"\n [subtitle]=\"'Refine your search terms or check your spelling.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n</c8y-data-grid>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "component", type: i1.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "component", type: i1.DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "loading", "columns", "rows", "pagination", "childNodePagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows", "treeGrid", "hideReload", "childNodesProperty", "parentNodeLabelProperty"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: AssetTableAutoRefreshComponent, selector: "c8y-asset-table-auto-refresh", inputs: ["config", "isIntervalRefresh", "refreshInterval", "isLoading", "isScrolling", "isRefreshDisabled"], outputs: ["onCountdownEnded"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }] }); }
1520
+ }
1521
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTableWidgetViewComponent, decorators: [{
1522
+ type: Component,
1523
+ args: [{ selector: 'c8y-asset-table-widget-view', standalone: true, imports: [CoreModule, RouterModule, AssetTableAutoRefreshComponent], template: "@if (!config.widgetInstanceGlobalTimeContext) {\n <c8y-asset-table-auto-refresh\n class=\"d-contents\"\n [isIntervalRefresh]=\"!!config.refreshInterval\"\n [refreshInterval]=\"config.refreshInterval\"\n [config]=\"config\"\n [isRefreshDisabled]=\"false\"\n [isLoading]=\"isLoading$\"\n (onCountdownEnded)=\"updateSelectedAssets()\"\n ></c8y-asset-table-auto-refresh>\n}\n<c8y-data-grid\n class=\"content-fullpage d-flex d-col border-top border-bottom\"\n [title]=\"config.title\"\n [displayOptions]=\"config.displayOptions\"\n [columns]=\"columns\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [pagination]=\"pagination\"\n (onPageSizeChange)=\"pagination = $event.pagination\"\n [refresh]=\"refresh\"\n>\n <c8y-ui-empty-state\n [icon]=\"'search'\"\n [title]=\"'No results to display.' | translate\"\n [subtitle]=\"'Refine your search terms or check your spelling.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n</c8y-data-grid>\n" }]
1524
+ }], ctorParameters: () => [], propDecorators: { config: [{
1525
+ type: Input
1526
+ }] } });
1527
+
1528
+ class IconRenderTypeModalComponent {
1529
+ constructor() {
1530
+ this.forms = [];
1531
+ this.initialIconConfig = [];
1532
+ this.labels = { ok: gettext('Save'), cancel: gettext('Cancel') };
1533
+ this.booleanOptions = [
1534
+ { label: gettext('True'), value: 'true' },
1535
+ { label: gettext('False'), value: 'false' }
1536
+ ];
1537
+ this.dataTypes = [
1538
+ { label: gettext('Number'), value: 'number' },
1539
+ { label: gettext('Date'), value: 'date' },
1540
+ { label: gettext('String'), value: 'string' },
1541
+ { label: gettext('Boolean'), value: 'boolean' }
1542
+ ];
1543
+ this.comparisonOptions = COMPARISON_OPTIONS;
1544
+ this.result = new Promise((resolve, reject) => {
1545
+ this._save = resolve;
1546
+ this._cancel = reject;
1547
+ });
1548
+ this.fb = inject(FormBuilder);
1549
+ }
1550
+ ngOnInit() {
1551
+ if (this.initialIconConfig && this.initialIconConfig.length > 0) {
1552
+ for (const config of this.initialIconConfig) {
1553
+ this.forms.push(this.createForm(config));
1554
+ }
1555
+ }
1556
+ else {
1557
+ this.addCondition();
1558
+ }
1559
+ }
1560
+ addCondition() {
1561
+ this.forms.push(this.createForm());
1562
+ }
1563
+ removeCondition(idx) {
1564
+ this.forms.splice(idx, 1);
1565
+ }
1566
+ iconSelectionChange(icon, idx) {
1567
+ this.forms[idx].get('icon')?.setValue(icon);
1568
+ }
1569
+ onDateSelected(date, idx) {
1570
+ this.forms[idx].get('value')?.setValue(date);
1571
+ }
1572
+ isAnyFormInvalid() {
1573
+ return this.forms.some(f => f.invalid);
1574
+ }
1575
+ onClose(_) {
1576
+ if (this.forms.every(f => f.valid)) {
1577
+ this._save(this.forms.map(f => f.value));
1578
+ }
1579
+ }
1580
+ onDismiss(_) {
1581
+ this._cancel();
1582
+ }
1583
+ createForm(config) {
1584
+ return this.fb.group({
1585
+ dataType: [config?.dataType || this.dataTypes[0], [Validators.required]],
1586
+ comparison: [config?.comparison || null, [Validators.required]],
1587
+ value: [config?.value ?? '', [Validators.required]],
1588
+ icon: [config?.icon || 'c8y-cockpit', [Validators.required]],
1589
+ color: [config?.color || '#212121', [Validators.required]]
1590
+ });
1591
+ }
1592
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: IconRenderTypeModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1593
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: IconRenderTypeModalComponent, isStandalone: true, selector: "c8y-icon-render-type-modal", ngImport: i0, template: "<c8y-modal\n title=\"{{ 'Configure icon conditions' | translate }}\"\n (onClose)=\"onClose($event)\"\n (onDismiss)=\"onDismiss($event)\"\n [labels]=\"labels\"\n [disabled]=\"isAnyFormInvalid()\"\n [headerClasses]=\"'dialog-header'\"\n>\n <ng-container c8y-modal-title>\n <span c8yIcon=\"c8y-circle-star\"></span>\n </ng-container>\n <form class=\"p-24 p-t-0\">\n @for (form of forms; track form; let i = $index) {\n <fieldset\n class=\"c8y-fieldset\"\n [formGroup]=\"form\"\n >\n <legend>\n {{ 'If condition' | translate }}\n @if (forms.length > 1) {\n <button\n class=\"btn-dot btn-dot--danger\"\n title=\"{{ 'Remove condition' | translate }}\"\n type=\"button\"\n (click)=\"removeCondition(i)\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n }\n </legend>\n <!-- Data type selection -->\n <div class=\"row tight-grid\">\n <div class=\"col-md-4 col-12\">\n <label\n for=\"dataType-{{ i }}\"\n translate\n >\n Data type\n </label>\n <c8y-select\n aria-label=\"{{ 'Data type select' | translate }}\"\n id=\"dataType-{{ i }}\"\n [items]=\"dataTypes\"\n [placeholder]=\"'Select data type\u2026' | translate\"\n formControlName=\"dataType\"\n ></c8y-select>\n </div>\n\n <!-- Comparison dropdown -->\n <div class=\"col-md-4 col-12\">\n <label\n for=\"comparison-{{ i }}\"\n translate\n >\n Comparison\n </label>\n <c8y-select\n aria-label=\"{{ 'Comparison select' | translate }}\"\n id=\"comparison-{{ i }}\"\n [items]=\"comparisonOptions[form.get('dataType')?.value?.value] || []\"\n formControlName=\"comparison\"\n [placeholder]=\"'Select comparison type\u2026' | translate\"\n labelProp=\"label\"\n valueProp=\"value\"\n ></c8y-select>\n </div>\n <!-- Value input based on type -->\n <div class=\"col-md-4 col-12\">\n <label\n for=\"value-{{ i }}\"\n translate\n >\n Value\n </label>\n @switch (form.get('dataType')?.value?.value) {\n @case ('number') {\n <input\n class=\"form-control\"\n id=\"value-{{ i }}\"\n type=\"number\"\n formControlName=\"value\"\n />\n }\n @case ('date') {\n <c8y-date-time-picker\n aria-label=\"{{ 'Date and time picker' | translate }}\"\n placeholder=\"{{ 'Select date\u2026' | translate }}\"\n formControlName=\"value\"\n size=\"sm\"\n (onDateSelected)=\"onDateSelected($event, i)\"\n ></c8y-date-time-picker>\n }\n @case ('string') {\n <input\n class=\"form-control\"\n id=\"value-{{ i }}\"\n type=\"text\"\n formControlName=\"value\"\n />\n }\n @case ('boolean') {\n <c8y-select\n aria-label=\"{{ 'Boolean select' | translate }}\"\n [items]=\"booleanOptions\"\n [placeholder]=\"'Select value\u2026' | translate\"\n formControlName=\"value\"\n labelProp=\"label\"\n valueProp=\"value\"\n ></c8y-select>\n }\n }\n </div>\n </div>\n <div class=\"d-flex gap-16 m-t-8 m-b-8\">\n <!-- Icon field -->\n <div class=\"d-flex a-i-center\">\n <label\n class=\"m-b-0 m-r-4\"\n for=\"icon-{{ i }}\"\n translate\n >\n Icon\n </label>\n <c8y-icon-selector-wrapper\n [selectedIcon]=\"form.get('icon')?.value || 'c8y-cockpit'\"\n [iconSize]=\"24\"\n (onSelect)=\"iconSelectionChange($event, i)\"\n ></c8y-icon-selector-wrapper>\n </div>\n\n <!-- Color field -->\n <div class=\"d-flex a-i-center m-l-8\">\n <label\n class=\"m-b-0 m-r-4 text-nowrap\"\n for=\"color-{{ i }}\"\n translate\n >\n Icon color\n </label>\n <input\n class=\"form-control\"\n style=\"width: 100%; min-width: 40px\"\n id=\"color-{{ i }}\"\n type=\"color\"\n formControlName=\"color\"\n />\n </div>\n </div>\n </fieldset>\n }\n <button\n class=\"btn btn-default m-t-16\"\n title=\"{{ 'Add condition' | translate }}\"\n type=\"button\"\n (click)=\"addCondition()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add condition' | translate }}\n </button>\n </form>\n</c8y-modal>\n", dependencies: [{ kind: "ngmodule", type: ModalModule }, { kind: "component", type: i1.ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$2.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: i2$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i1.SelectComponent, selector: "c8y-select", inputs: ["placeholder", "items", "selected", "container", "multi", "canSelectWithSpace", "disabled", "autoClose", "insideClick", "required", "canDeselect", "name", "icon", "filterItems"], outputs: ["onSelect", "onDeselect", "onIconClick"] }, { kind: "ngmodule", type: IconSelectorModule }, { kind: "component", type: i3$1.IconSelectorWrapperComponent, selector: "c8y-icon-selector-wrapper", inputs: ["canRemoveIcon", "selectedIcon", "iconSize"], outputs: ["onSelect"] }, { kind: "ngmodule", type: DateTimePickerModule }, { kind: "component", type: i1.DateTimePickerComponent, selector: "c8y-date-time-picker", inputs: ["minDate", "maxDate", "placeholder", "dateInputFormat", "adaptivePosition", "size", "dateType", "config"], outputs: ["onDateSelected"] }, { kind: "ngmodule", type: C8yTranslateModule }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }] }); }
1594
+ }
1595
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: IconRenderTypeModalComponent, decorators: [{
1596
+ type: Component,
1597
+ args: [{ selector: 'c8y-icon-render-type-modal', standalone: true, imports: [
1598
+ ModalModule,
1599
+ FormsModule,
1600
+ ReactiveFormsModule,
1601
+ SelectModule,
1602
+ IconSelectorModule,
1603
+ DateTimePickerModule,
1604
+ C8yTranslateModule,
1605
+ CommonModule,
1606
+ CoreModule
1607
+ ], template: "<c8y-modal\n title=\"{{ 'Configure icon conditions' | translate }}\"\n (onClose)=\"onClose($event)\"\n (onDismiss)=\"onDismiss($event)\"\n [labels]=\"labels\"\n [disabled]=\"isAnyFormInvalid()\"\n [headerClasses]=\"'dialog-header'\"\n>\n <ng-container c8y-modal-title>\n <span c8yIcon=\"c8y-circle-star\"></span>\n </ng-container>\n <form class=\"p-24 p-t-0\">\n @for (form of forms; track form; let i = $index) {\n <fieldset\n class=\"c8y-fieldset\"\n [formGroup]=\"form\"\n >\n <legend>\n {{ 'If condition' | translate }}\n @if (forms.length > 1) {\n <button\n class=\"btn-dot btn-dot--danger\"\n title=\"{{ 'Remove condition' | translate }}\"\n type=\"button\"\n (click)=\"removeCondition(i)\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n }\n </legend>\n <!-- Data type selection -->\n <div class=\"row tight-grid\">\n <div class=\"col-md-4 col-12\">\n <label\n for=\"dataType-{{ i }}\"\n translate\n >\n Data type\n </label>\n <c8y-select\n aria-label=\"{{ 'Data type select' | translate }}\"\n id=\"dataType-{{ i }}\"\n [items]=\"dataTypes\"\n [placeholder]=\"'Select data type\u2026' | translate\"\n formControlName=\"dataType\"\n ></c8y-select>\n </div>\n\n <!-- Comparison dropdown -->\n <div class=\"col-md-4 col-12\">\n <label\n for=\"comparison-{{ i }}\"\n translate\n >\n Comparison\n </label>\n <c8y-select\n aria-label=\"{{ 'Comparison select' | translate }}\"\n id=\"comparison-{{ i }}\"\n [items]=\"comparisonOptions[form.get('dataType')?.value?.value] || []\"\n formControlName=\"comparison\"\n [placeholder]=\"'Select comparison type\u2026' | translate\"\n labelProp=\"label\"\n valueProp=\"value\"\n ></c8y-select>\n </div>\n <!-- Value input based on type -->\n <div class=\"col-md-4 col-12\">\n <label\n for=\"value-{{ i }}\"\n translate\n >\n Value\n </label>\n @switch (form.get('dataType')?.value?.value) {\n @case ('number') {\n <input\n class=\"form-control\"\n id=\"value-{{ i }}\"\n type=\"number\"\n formControlName=\"value\"\n />\n }\n @case ('date') {\n <c8y-date-time-picker\n aria-label=\"{{ 'Date and time picker' | translate }}\"\n placeholder=\"{{ 'Select date\u2026' | translate }}\"\n formControlName=\"value\"\n size=\"sm\"\n (onDateSelected)=\"onDateSelected($event, i)\"\n ></c8y-date-time-picker>\n }\n @case ('string') {\n <input\n class=\"form-control\"\n id=\"value-{{ i }}\"\n type=\"text\"\n formControlName=\"value\"\n />\n }\n @case ('boolean') {\n <c8y-select\n aria-label=\"{{ 'Boolean select' | translate }}\"\n [items]=\"booleanOptions\"\n [placeholder]=\"'Select value\u2026' | translate\"\n formControlName=\"value\"\n labelProp=\"label\"\n valueProp=\"value\"\n ></c8y-select>\n }\n }\n </div>\n </div>\n <div class=\"d-flex gap-16 m-t-8 m-b-8\">\n <!-- Icon field -->\n <div class=\"d-flex a-i-center\">\n <label\n class=\"m-b-0 m-r-4\"\n for=\"icon-{{ i }}\"\n translate\n >\n Icon\n </label>\n <c8y-icon-selector-wrapper\n [selectedIcon]=\"form.get('icon')?.value || 'c8y-cockpit'\"\n [iconSize]=\"24\"\n (onSelect)=\"iconSelectionChange($event, i)\"\n ></c8y-icon-selector-wrapper>\n </div>\n\n <!-- Color field -->\n <div class=\"d-flex a-i-center m-l-8\">\n <label\n class=\"m-b-0 m-r-4 text-nowrap\"\n for=\"color-{{ i }}\"\n translate\n >\n Icon color\n </label>\n <input\n class=\"form-control\"\n style=\"width: 100%; min-width: 40px\"\n id=\"color-{{ i }}\"\n type=\"color\"\n formControlName=\"color\"\n />\n </div>\n </div>\n </fieldset>\n }\n <button\n class=\"btn btn-default m-t-16\"\n title=\"{{ 'Add condition' | translate }}\"\n type=\"button\"\n (click)=\"addCondition()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add condition' | translate }}\n </button>\n </form>\n</c8y-modal>\n" }]
1608
+ }] });
1609
+
1610
+ class OperationRenderTypeModalComponent {
1611
+ constructor() {
1612
+ this.showButtonLabelOnly = false;
1613
+ this.labels = { ok: gettext('Save'), cancel: gettext('Cancel') };
1614
+ this.operationList = [
1615
+ { label: gettext('Restart device'), value: 'restart' },
1616
+ { label: gettext('Firmware update'), value: 'firmwareUpdate' },
1617
+ { label: gettext('Send command'), value: 'sendCommand' }
1618
+ ];
1619
+ this.result = new Promise((resolve, reject) => {
1620
+ this._save = resolve;
1621
+ this._cancel = reject;
1622
+ });
1623
+ this.fb = inject(FormBuilder);
1624
+ this.translate = inject(TranslateService);
1625
+ }
1626
+ get modalTitle() {
1627
+ if (this.showButtonLabelOnly) {
1628
+ return this.translate.instant(gettext('Maintenance mode'));
1629
+ }
1630
+ const buttonLabel = this.form?.get('buttonLabel')?.value;
1631
+ return buttonLabel
1632
+ ? this.translate.instant(gettext('Edit operation'))
1633
+ : this.translate.instant(gettext('Create operation'));
1634
+ }
1635
+ ngOnInit() {
1636
+ const defaultCommand = JSON.stringify({
1637
+ description: 'Command description',
1638
+ c8y_Command: {
1639
+ text: '<command>'
1640
+ }
1641
+ }, null, 2);
1642
+ this.form = this.fb.group({
1643
+ buttonLabel: new FormControl(this.initialConfig?.buttonLabel ?? '', {
1644
+ validators: [Validators.required]
1645
+ }),
1646
+ operation: new FormControl(this.initialConfig?.operationType ?? null),
1647
+ command: new FormControl(this.initialConfig?.command ?? defaultCommand, {
1648
+ validators: [Validators.required]
1649
+ })
1650
+ });
1651
+ }
1652
+ onClose(_) {
1653
+ if (this.form.valid) {
1654
+ this._save(this.form.getRawValue());
1655
+ }
1656
+ }
1657
+ onDismiss(_) {
1658
+ this._cancel();
1659
+ }
1660
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: OperationRenderTypeModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1661
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: OperationRenderTypeModalComponent, isStandalone: true, selector: "c8y-operation-render-type-modal", ngImport: i0, template: "<c8y-modal\n [title]=\"modalTitle\"\n (onClose)=\"onClose($event)\"\n (onDismiss)=\"onDismiss($event)\"\n [labels]=\"labels\"\n [disabled]=\"form?.invalid\"\n [headerClasses]=\"'dialog-header'\"\n>\n <ng-container c8y-modal-title>\n <span [c8yIcon]=\"'cog'\"></span>\n </ng-container>\n <form\n class=\"p-24\"\n [formGroup]=\"form\"\n >\n <c8y-form-group>\n <label\n for=\"buttonLabel\"\n translate\n >\n Button label\n </label>\n <input\n class=\"form-control\"\n id=\"buttonLabel\"\n placeholder=\"{{ 'e.g. Execute operation' | translate }}\"\n type=\"text\"\n formControlName=\"buttonLabel\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n @if (!showButtonLabelOnly) {\n <div class=\"form-group\">\n <label\n for=\"operation\"\n translate\n >\n Operation\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"operation\"\n formControlName=\"operation\"\n >\n @for (op of operationList; track op) {\n <option [ngValue]=\"op.value\">\n {{ op.label | translate }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group\">\n <label\n for=\"command\"\n translate\n >\n Command\n </label>\n <textarea\n class=\"form-control no-resize inner-scroll\"\n style=\"max-height: 300px\"\n id=\"command\"\n c8y-textarea-autoresize\n formControlName=\"command\"\n maxlength=\"900\"\n ></textarea>\n </div>\n }\n </form>\n</c8y-modal>\n", dependencies: [{ kind: "ngmodule", type: ModalModule }, { kind: "component", type: i1.ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.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: i2$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: C8yTranslateModule }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i1.TextareaAutoresizeDirective, selector: "[c8y-textarea-autoresize]" }, { kind: "ngmodule", type: CoreModule }, { kind: "component", type: i1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: i1.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }] }); }
1662
+ }
1663
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: OperationRenderTypeModalComponent, decorators: [{
1664
+ type: Component,
1665
+ args: [{ selector: 'c8y-operation-render-type-modal', standalone: true, imports: [
1666
+ ModalModule,
1667
+ FormsModule,
1668
+ ReactiveFormsModule,
1669
+ C8yTranslateModule,
1670
+ CommonModule,
1671
+ CoreModule
1672
+ ], template: "<c8y-modal\n [title]=\"modalTitle\"\n (onClose)=\"onClose($event)\"\n (onDismiss)=\"onDismiss($event)\"\n [labels]=\"labels\"\n [disabled]=\"form?.invalid\"\n [headerClasses]=\"'dialog-header'\"\n>\n <ng-container c8y-modal-title>\n <span [c8yIcon]=\"'cog'\"></span>\n </ng-container>\n <form\n class=\"p-24\"\n [formGroup]=\"form\"\n >\n <c8y-form-group>\n <label\n for=\"buttonLabel\"\n translate\n >\n Button label\n </label>\n <input\n class=\"form-control\"\n id=\"buttonLabel\"\n placeholder=\"{{ 'e.g. Execute operation' | translate }}\"\n type=\"text\"\n formControlName=\"buttonLabel\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n @if (!showButtonLabelOnly) {\n <div class=\"form-group\">\n <label\n for=\"operation\"\n translate\n >\n Operation\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"operation\"\n formControlName=\"operation\"\n >\n @for (op of operationList; track op) {\n <option [ngValue]=\"op.value\">\n {{ op.label | translate }}\n </option>\n }\n </select>\n </div>\n </div>\n <div class=\"form-group\">\n <label\n for=\"command\"\n translate\n >\n Command\n </label>\n <textarea\n class=\"form-control no-resize inner-scroll\"\n style=\"max-height: 300px\"\n id=\"command\"\n c8y-textarea-autoresize\n formControlName=\"command\"\n maxlength=\"900\"\n ></textarea>\n </div>\n }\n </form>\n</c8y-modal>\n" }]
1673
+ }] });
1674
+
1675
+ class AssetTableGridSettingsComponent {
1676
+ constructor() {
1677
+ this.columns = [];
1678
+ this.selectedProperties = [];
1679
+ this.operationColumns = [];
1680
+ this.text = '';
1681
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1682
+ this.existingColumnFilter = {};
1683
+ this.hasMaintenanceColumn = false;
1684
+ this.customHeaderNames = {};
1685
+ this.customLabel = gettext('(custom`column`)');
1686
+ this.widgetConfigService = inject(WidgetConfigService);
1687
+ this.assetTableService = inject(AssetTableService);
1688
+ this.modalService = inject(BsModalService);
1689
+ this.destroy$ = new Subject();
1690
+ }
1691
+ ngOnInit() {
1692
+ this.widgetConfigService.currentConfig$.pipe(takeUntil(this.destroy$)).subscribe(config => {
1693
+ this.selectedProperties = config?.selectedProperties || [];
1694
+ this.operationColumns = config?.operationColumns || [];
1695
+ this.existingColumnFilter = config?.filterPredicate || {};
1696
+ this.customHeaderNames = config?.customHeaderNames || {};
1697
+ this.columns = this.getColumns(config);
1698
+ this.applyCustomHeaderNames();
1699
+ this.columns.forEach(column => {
1700
+ column.header = this.customHeaderNames[column.name] || column.header;
1701
+ if (column.operationType === 'maintenance') {
1702
+ this.hasMaintenanceColumn = true;
1703
+ }
1704
+ if (column.filteringConfig) {
1705
+ column.filteringConfig.formGroup = new FormGroup({});
1706
+ column.filteringConfig.model = this.existingColumnFilter[column.name] || {};
1707
+ }
1708
+ });
1709
+ });
1710
+ }
1711
+ ngOnDestroy() {
1712
+ this.destroy$.next();
1713
+ this.destroy$.complete();
1714
+ }
1715
+ async applyColumnType(type, column) {
1716
+ if (type === 'icon') {
1717
+ const currentIconConfig = this.selectedProperties.find(
1718
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1719
+ (prop) => prop.name === column.name)?.iconConfig;
1720
+ const modal = this.modalService.show(IconRenderTypeModalComponent, {
1721
+ class: 'modal-md',
1722
+ ariaDescribedby: 'modal-body',
1723
+ ariaLabelledBy: 'modal-title',
1724
+ ignoreBackdropClick: true,
1725
+ initialState: {
1726
+ initialIconConfig: currentIconConfig || []
1727
+ }
1728
+ }).content;
1729
+ try {
1730
+ const result = await modal.result;
1731
+ this.selectedProperties = this.selectedProperties.map(prop => prop.name === column.name || prop.title === column.name
1732
+ ? { ...prop, iconConfig: result, columnType: 'icon' }
1733
+ : prop);
1734
+ }
1735
+ catch (ex) {
1736
+ return;
1737
+ }
1738
+ }
1739
+ else {
1740
+ this.selectedProperties = this.selectedProperties.map(prop => prop.name === column.name ? { ...prop, columnType: type } : prop);
1741
+ }
1742
+ this.widgetConfigService.updateConfig({ selectedProperties: [...this.selectedProperties] });
1743
+ }
1744
+ onColumnDrop(event) {
1745
+ moveItemInArray(this.columns, event.previousIndex, event.currentIndex);
1746
+ const newOrder = this.columns.map((column) => ({
1747
+ __origin: column.__origin,
1748
+ __id: column.name
1749
+ }));
1750
+ this.widgetConfigService.updateConfig({ columnOrder: newOrder });
1751
+ }
1752
+ toggleColumnVisibility(column) {
1753
+ this.selectedProperties = this.selectedProperties.map(prop => prop.name === column.name ? { ...prop, visible: column.visible } : prop);
1754
+ this.widgetConfigService.updateConfig({ selectedProperties: [...this.selectedProperties] });
1755
+ }
1756
+ applyFilter(column, dropdown) {
1757
+ const formGroup = column.filteringConfig?.formGroup;
1758
+ this.existingColumnFilter[column.name] = formGroup?.value;
1759
+ column.filteringConfig.model = formGroup?.value;
1760
+ // Update the model so Formly renders it next time
1761
+ // Update config and local copy
1762
+ this.widgetConfigService.updateConfig({ filterPredicate: this.existingColumnFilter });
1763
+ dropdown.hide();
1764
+ }
1765
+ resetFilter(column, dropdown) {
1766
+ delete this.existingColumnFilter[column.name];
1767
+ column.filteringConfig.formGroup.reset();
1768
+ this.widgetConfigService.updateConfig({ filterPredicate: this.existingColumnFilter });
1769
+ dropdown.hide();
1770
+ }
1771
+ isColumnFiltered(column) {
1772
+ const value = this.existingColumnFilter?.[column.name];
1773
+ if (!value)
1774
+ return false;
1775
+ // Check if the filter has a meaningful value (non-empty)
1776
+ return Object.values(value).some(v => v !== null && v !== '' && v !== false);
1777
+ }
1778
+ updateColumnNames(column) {
1779
+ this.customHeaderNames = { ...this.customHeaderNames, [column.name]: column.header };
1780
+ this.applyCustomHeaderNames();
1781
+ this.widgetConfigService.updateConfig({
1782
+ selectedProperties: this.selectedProperties,
1783
+ operationColumns: this.operationColumns,
1784
+ customHeaderNames: this.customHeaderNames
1785
+ });
1786
+ }
1787
+ changeSortOrder(column) {
1788
+ const currentOrder = column.sortOrder;
1789
+ let nextOrder = '';
1790
+ if (!currentOrder) {
1791
+ nextOrder = 'asc';
1792
+ }
1793
+ else if (currentOrder === 'asc') {
1794
+ nextOrder = 'desc';
1795
+ }
1796
+ else if (currentOrder === 'desc') {
1797
+ nextOrder = '';
1798
+ }
1799
+ column.sortOrder = nextOrder || null;
1800
+ this.selectedProperties = this.selectedProperties.map(prop => {
1801
+ if (prop.name === column.name) {
1802
+ if (nextOrder) {
1803
+ prop.sortOrder = nextOrder;
1804
+ }
1805
+ else {
1806
+ delete prop.sortOrder;
1807
+ }
1808
+ }
1809
+ return prop;
1810
+ });
1811
+ const columnSortOrders = {};
1812
+ this.columns.forEach(c => {
1813
+ if (c.sortOrder) {
1814
+ columnSortOrders[c.name] = c.sortOrder;
1815
+ }
1816
+ });
1817
+ this.widgetConfigService.updateConfig({
1818
+ selectedProperties: this.selectedProperties,
1819
+ columnSortOrders
1820
+ });
1821
+ }
1822
+ async onCreateOperation() {
1823
+ const modal = this.operationModal(false);
1824
+ const result = (await modal.result);
1825
+ result.operationType = 'operation';
1826
+ result.header = gettext('Create operation');
1827
+ this.operationColumns.push(result);
1828
+ this.widgetConfigService.updateConfig({ operationColumns: this.operationColumns });
1829
+ }
1830
+ async onToggleMaintenanceMode() {
1831
+ const modal = this.operationModal(true);
1832
+ const result = (await modal.result);
1833
+ result.operationType = 'maintenance';
1834
+ result.header = gettext('Toggle maintenance mode');
1835
+ this.operationColumns.push(result);
1836
+ this.widgetConfigService.updateConfig({ operationColumns: this.operationColumns });
1837
+ }
1838
+ async editOperationColumn(column) {
1839
+ const currentOpConfig = this.operationColumns.find(op => op.buttonLabel === column.name);
1840
+ const modal = this.operationModal(currentOpConfig.operationType === 'maintenance', currentOpConfig);
1841
+ const result = await modal.result;
1842
+ this.operationColumns = this.operationColumns.map(op => op.buttonLabel === column.name ? result : op);
1843
+ this.widgetConfigService.updateConfig({ operationColumns: this.operationColumns });
1844
+ }
1845
+ deleteColumn(column) {
1846
+ this.operationColumns = this.operationColumns.filter(opCol => opCol.buttonLabel !== column.name);
1847
+ if (column.operationType === 'maintenance') {
1848
+ this.hasMaintenanceColumn = false;
1849
+ }
1850
+ this.widgetConfigService.updateConfig({ operationColumns: this.operationColumns });
1851
+ }
1852
+ trackByColumnName(_index, column) {
1853
+ return `${column.name}-${_index}`;
1854
+ }
1855
+ applyCustomHeaderNames() {
1856
+ // Update selected properties and operation columns based on custom header names
1857
+ this.selectedProperties = this.selectedProperties.map(prop => {
1858
+ const newLabel = this.customHeaderNames[prop.name];
1859
+ if (newLabel) {
1860
+ prop.label = newLabel;
1861
+ }
1862
+ return prop;
1863
+ });
1864
+ this.operationColumns = this.operationColumns.map(opCol => {
1865
+ const newHeader = this.customHeaderNames[opCol.buttonLabel];
1866
+ if (newHeader) {
1867
+ opCol.header = newHeader;
1868
+ }
1869
+ return opCol;
1870
+ });
1871
+ }
1872
+ operationModal(showButtonLabelOnly, operationConfig) {
1873
+ return this.modalService.show(OperationRenderTypeModalComponent, {
1874
+ class: 'modal-sm',
1875
+ ariaDescribedby: 'modal-body',
1876
+ ariaLabelledBy: 'modal-title',
1877
+ ignoreBackdropClick: true,
1878
+ initialState: {
1879
+ initialConfig: operationConfig || {},
1880
+ showButtonLabelOnly
1881
+ }
1882
+ }).content;
1883
+ }
1884
+ getColumns(config) {
1885
+ return this.assetTableService.getColumns(this.selectedProperties, this.operationColumns, config);
1886
+ }
1887
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTableGridSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1888
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AssetTableGridSettingsComponent, isStandalone: true, selector: "c8y-asset-table-column-settings", host: { classAttribute: "bg-level-1" }, providers: [BsDropdownDirective], ngImport: i0, template: "<c8y-list-group\n class=\"inner-scroll bg-inherit d-block no-border-last separator-top\"\n style=\"max-height: 300px\"\n role=\"list\"\n cdkDropList\n (cdkDropListDropped)=\"onColumnDrop($event)\"\n>\n @for (column of columns; track trackByColumnName($index, column)) {\n @if (!column.positionFixed) {\n <c8y-li\n role=\"listitem\"\n cdkDrag\n cdkDragLockAxis=\"y\"\n >\n <c8y-li-drag-handle\n title=\"{{ 'Drag to reorder' | translate }}\"\n [attr.aria-label]=\"'Drag to reorder' | translate\"\n cdkDragHandle\n >\n <i\n c8yIcon=\"drag-reorder\"\n style=\"font-size: 0.7em\"\n ></i>\n </c8y-li-drag-handle>\n @if (column.__origin !== 'status') {\n <c8y-li-checkbox\n class=\"a-s-stretch\"\n title=\"{{ (column.header | translate) || column.name }} {{\n column.custom ? (customLabel | translate) : ''\n }}\"\n [attr.aria-label]=\"'Show/hide column' | translate\"\n [(ngModel)]=\"column.visible\"\n (ngModelChange)=\"toggleColumnVisibility(column)\"\n c8yProductExperience\n [attr.data-cy]=\"'data-grid--custom-column-header-' + column.header\"\n ></c8y-li-checkbox>\n } @else {\n <c8y-li-checkbox class=\"a-s-stretch no-pointer invisible\"></c8y-li-checkbox>\n }\n <div class=\"content-flex-32 p-t-4 p-b-4\">\n <div class=\"col-6\">\n @if (column.__origin !== 'status') {\n <label class=\"editable\">\n <input\n class=\"form-control input-sm\"\n [attr.aria-label]=\"'Column header' | translate\"\n placeholder=\"{{ 'Column header' | translate }}\"\n type=\"text\"\n autocomplete=\"off\"\n required\n [(ngModel)]=\"column.header\"\n (keydown.enter)=\"updateColumnNames(column); $event.target.blur()\"\n (blur)=\"updateColumnNames(column)\"\n />\n </label>\n } @else {\n <p class=\"text-12 p-t-4 p-b-4\">{{ column.header | translate }}</p>\n }\n </div>\n <div class=\"col-6\">\n <div class=\"d-flex a-i-center gap-8 p-t-4 p-b-4\">\n <!-- Filters button -->\n @if (column.filteringConfig) {\n <div class=\"d-flex a-i-center gap-4 flex-no-shrink\">\n <div\n class=\"dropdown d-flex\"\n container=\"body\"\n dropdown\n [insideClick]=\"true\"\n c8yDropdownFocusTrap\n #gridHeaderDropdown=\"bs-dropdown\"\n >\n <button\n class=\"dropdown-toggle btn btn-icon btn-default btn-xs c8y-dropdown\"\n [attr.aria-label]=\"'Filter' | translate\"\n tooltip=\"{{ 'Filter' | translate }}\"\n placement=\"top\"\n container=\"body\"\n [attr.aria-haspopup]=\"true\"\n [attr.aria-expanded]=\"gridHeaderDropdown.isOpen\"\n type=\"button\"\n [delay]=\"500\"\n [ngClass]=\"{ active: isColumnFiltered(column) }\"\n dropdownToggle\n >\n <i c8yIcon=\"filter\"></i>\n </button>\n <div\n class=\"dropdown-menu\"\n (click)=\"$event.stopPropagation()\"\n *dropdownMenu\n >\n <formly-form\n class=\"p-16 d-block\"\n style=\"min-width: 250px\"\n [form]=\"column.filteringConfig.formGroup\"\n [fields]=\"column.filteringConfig.fields\"\n [model]=\"column.filteringConfig.model\"\n ></formly-form>\n <div class=\"p-16 fit-w d-flex separator-top\">\n <button\n class=\"btn btn-default btn-sm m-r-8 grow\"\n title=\"{{ 'Reset' | translate }}\"\n (click)=\"resetFilter(column, gridHeaderDropdown)\"\n translate\n >\n Reset\n </button>\n <button\n class=\"btn btn-primary btn-sm grow\"\n title=\"{{ 'Apply' | translate }}\"\n (click)=\"applyFilter(column, gridHeaderDropdown)\"\n translate\n >\n Apply\n </button>\n </div>\n </div>\n </div>\n </div>\n }\n <!-- Sort button -->\n <div class=\"d-flex a-i-center gap-4 flex-no-shrink\">\n <button\n class=\"btn btn-icon btn-default btn-xs\"\n [attr.aria-label]=\"'Sort order' | translate\"\n tooltip=\"{{ 'Sort order' | translate }}\"\n placement=\"top\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"changeSortOrder(column)\"\n >\n @if (column.sortOrder === 'asc') {\n <i c8yIcon=\"long-arrow-up\"></i>\n } @else if (column.sortOrder === 'desc') {\n <i c8yIcon=\"long-arrow-down\"></i>\n } @else {\n <i\n c8yIcon=\"exchange\"\n style=\"transform: rotate(90deg)\"\n ></i>\n }\n </button>\n </div>\n <!-- Column type dropdown -->\n @if (!column.isOperation && column.__origin !== 'status') {\n <div\n class=\"d-flex a-i-center gap-4 flex-no-shrink\"\n [ngClass]=\"{ 'no-pointer invisible': column.__origin === 'status' }\"\n >\n <div\n class=\"dropdown d-flex\"\n container=\"body\"\n dropdown\n c8yDropdownFocusTrap\n #columnTypeDropdown=\"bs-dropdown\"\n >\n <button\n class=\"btn btn-default btn-xs dropdown-toggle c8y-dropdown\"\n [attr.aria-label]=\"'Select render type' | translate\"\n tooltip=\"{{ 'Render type' | translate }}\"\n placement=\"top\"\n container=\"body\"\n [attr.aria-haspopup]=\"true\"\n [attr.aria-expanded]=\"columnTypeDropdown.isOpen\"\n type=\"button\"\n [delay]=\"500\"\n dropdownToggle\n >\n {{ column.type | translate | humanize }}\n <i c8yIcon=\"caret-down\"></i>\n </button>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n >\n <c8y-list-group role=\"list\">\n @if (column.type === 'computed' || column.computedConfig) {\n <c8y-li\n tabindex=\"0\"\n role=\"listitem\"\n (keydown.enter)=\"\n applyColumnType('computed', column); $event.preventDefault()\n \"\n (keydown.space)=\"\n applyColumnType('computed', column); $event.preventDefault()\n \"\n (click)=\"applyColumnType('computed', column)\"\n [active]=\"column.type === 'computed'\"\n >\n {{ 'Computed' | translate }}\n </c8y-li>\n <c8y-li\n tabindex=\"0\"\n role=\"listitem\"\n (click)=\"applyColumnType('icon', column)\"\n (keydown.enter)=\"\n applyColumnType('icon', column); $event.preventDefault()\n \"\n (keydown.space)=\"\n applyColumnType('icon', column); $event.preventDefault()\n \"\n [active]=\"column.type === 'icon'\"\n >\n {{ 'Icon' | translate }}\n </c8y-li>\n } @else {\n <c8y-li\n tabindex=\"0\"\n role=\"listitem\"\n (click)=\"applyColumnType('default', column)\"\n (keydown.enter)=\"\n applyColumnType('default', column); $event.preventDefault()\n \"\n (keydown.space)=\"\n applyColumnType('default', column); $event.preventDefault()\n \"\n [active]=\"column.type === 'default'\"\n >\n {{ 'Default' | translate }}\n </c8y-li>\n <c8y-li\n tabindex=\"0\"\n role=\"listitem\"\n (click)=\"applyColumnType('date', column)\"\n (keydown.enter)=\"\n applyColumnType('date', column); $event.preventDefault()\n \"\n (keydown.space)=\"\n applyColumnType('date', column); $event.preventDefault()\n \"\n [active]=\"column.type === 'date'\"\n >\n {{ 'Date' | translate }}\n </c8y-li>\n <c8y-li\n tabindex=\"0\"\n role=\"listitem\"\n (click)=\"applyColumnType('icon', column)\"\n [active]=\"column.type === 'icon'\"\n (keydown.enter)=\"\n applyColumnType('icon', column); $event.preventDefault()\n \"\n (keydown.space)=\"\n applyColumnType('icon', column); $event.preventDefault()\n \"\n >\n {{ 'Icon' | translate }}\n </c8y-li>\n }\n </c8y-list-group>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n @if (column.isOperation) {\n <ng-container>\n @if (true) {\n <c8y-li-action\n icon=\"edit1\"\n [attr.aria-label]=\"'Edit' | translate\"\n label=\"Edit\"\n (click)=\"editOperationColumn(column)\"\n ></c8y-li-action>\n }\n @if (true) {\n <c8y-li-action\n icon=\"delete\"\n [attr.aria-label]=\"'Delete' | translate\"\n label=\"Delete\"\n (click)=\"deleteColumn(column)\"\n ></c8y-li-action>\n }\n </ng-container>\n }\n </c8y-li>\n }\n }\n @if (columns.length === 0) {\n <c8y-ui-empty-state\n [icon]=\"'list'\"\n [title]=\"'No properties selected.' | translate\"\n [subtitle]=\"'Select from the list which columns to display.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n }\n</c8y-list-group>\n<div class=\"m-b-16 p-t-16 p-l-24 p-r-16 fit-w separator-top\">\n <div\n class=\"dropdown\"\n container=\"body\"\n dropdown\n [insideClick]=\"true\"\n c8yDropdownFocusTrap\n #customActionsDropdown=\"bs-dropdown\"\n >\n <button\n class=\"btn btn-default btn-sm dropdown-toggle c8y-dropdown\"\n [title]=\"'Add action' | translate\"\n [attr.aria-haspopup]=\"true\"\n [attr.aria-expanded]=\"customActionsDropdown.isOpen\"\n type=\"button\"\n dropdownToggle\n >\n <i c8yIcon=\"cog\"></i>\n <span>{{ 'Add action' | translate }}</span>\n <span class=\"caret\"></span>\n </button>\n <ul\n class=\"dropdown-menu\"\n *dropdownMenu\n >\n <li>\n <button\n class=\"dropdown-item\"\n type=\"button\"\n (click)=\"onCreateOperation()\"\n >\n {{ 'Create operation' | translate }}\n </button>\n </li>\n <li>\n <button\n class=\"dropdown-item\"\n type=\"button\"\n (click)=\"onToggleMaintenanceMode()\"\n [disabled]=\"hasMaintenanceColumn\"\n >\n {{ 'Toggle maintenance mode' | translate }}\n </button>\n </li>\n </ul>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.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: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3.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"] }, { kind: "ngmodule", type: BsDropdownModule }, { kind: "directive", type: i3$2.BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: i3$2.BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: i3$2.BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: DropdownFocusTrapDirective, selector: "[c8yDropdownFocusTrap]" }, { kind: "directive", type: 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: 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: "ngmodule", type: FormlyModule }, { kind: "component", type: i4.FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { 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.ListItemActionComponent, selector: "c8y-list-item-action, c8y-li-action", inputs: ["label", "icon", "disabled"], outputs: ["click"] }, { 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: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: HumanizePipe, name: "humanize" }] }); }
1889
+ }
1890
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTableGridSettingsComponent, decorators: [{
1891
+ type: Component,
1892
+ args: [{ selector: 'c8y-asset-table-column-settings', host: {
1893
+ class: 'bg-level-1'
1894
+ }, imports: [
1895
+ FormsModule,
1896
+ C8yTranslatePipe,
1897
+ TooltipModule,
1898
+ HumanizePipe,
1899
+ BsDropdownModule,
1900
+ IconDirective,
1901
+ DropdownFocusTrapDirective,
1902
+ CdkDropList,
1903
+ CdkDrag,
1904
+ FormlyModule,
1905
+ EmptyStateComponent,
1906
+ ListGroupModule,
1907
+ NgClass
1908
+ ], providers: [BsDropdownDirective], template: "<c8y-list-group\n class=\"inner-scroll bg-inherit d-block no-border-last separator-top\"\n style=\"max-height: 300px\"\n role=\"list\"\n cdkDropList\n (cdkDropListDropped)=\"onColumnDrop($event)\"\n>\n @for (column of columns; track trackByColumnName($index, column)) {\n @if (!column.positionFixed) {\n <c8y-li\n role=\"listitem\"\n cdkDrag\n cdkDragLockAxis=\"y\"\n >\n <c8y-li-drag-handle\n title=\"{{ 'Drag to reorder' | translate }}\"\n [attr.aria-label]=\"'Drag to reorder' | translate\"\n cdkDragHandle\n >\n <i\n c8yIcon=\"drag-reorder\"\n style=\"font-size: 0.7em\"\n ></i>\n </c8y-li-drag-handle>\n @if (column.__origin !== 'status') {\n <c8y-li-checkbox\n class=\"a-s-stretch\"\n title=\"{{ (column.header | translate) || column.name }} {{\n column.custom ? (customLabel | translate) : ''\n }}\"\n [attr.aria-label]=\"'Show/hide column' | translate\"\n [(ngModel)]=\"column.visible\"\n (ngModelChange)=\"toggleColumnVisibility(column)\"\n c8yProductExperience\n [attr.data-cy]=\"'data-grid--custom-column-header-' + column.header\"\n ></c8y-li-checkbox>\n } @else {\n <c8y-li-checkbox class=\"a-s-stretch no-pointer invisible\"></c8y-li-checkbox>\n }\n <div class=\"content-flex-32 p-t-4 p-b-4\">\n <div class=\"col-6\">\n @if (column.__origin !== 'status') {\n <label class=\"editable\">\n <input\n class=\"form-control input-sm\"\n [attr.aria-label]=\"'Column header' | translate\"\n placeholder=\"{{ 'Column header' | translate }}\"\n type=\"text\"\n autocomplete=\"off\"\n required\n [(ngModel)]=\"column.header\"\n (keydown.enter)=\"updateColumnNames(column); $event.target.blur()\"\n (blur)=\"updateColumnNames(column)\"\n />\n </label>\n } @else {\n <p class=\"text-12 p-t-4 p-b-4\">{{ column.header | translate }}</p>\n }\n </div>\n <div class=\"col-6\">\n <div class=\"d-flex a-i-center gap-8 p-t-4 p-b-4\">\n <!-- Filters button -->\n @if (column.filteringConfig) {\n <div class=\"d-flex a-i-center gap-4 flex-no-shrink\">\n <div\n class=\"dropdown d-flex\"\n container=\"body\"\n dropdown\n [insideClick]=\"true\"\n c8yDropdownFocusTrap\n #gridHeaderDropdown=\"bs-dropdown\"\n >\n <button\n class=\"dropdown-toggle btn btn-icon btn-default btn-xs c8y-dropdown\"\n [attr.aria-label]=\"'Filter' | translate\"\n tooltip=\"{{ 'Filter' | translate }}\"\n placement=\"top\"\n container=\"body\"\n [attr.aria-haspopup]=\"true\"\n [attr.aria-expanded]=\"gridHeaderDropdown.isOpen\"\n type=\"button\"\n [delay]=\"500\"\n [ngClass]=\"{ active: isColumnFiltered(column) }\"\n dropdownToggle\n >\n <i c8yIcon=\"filter\"></i>\n </button>\n <div\n class=\"dropdown-menu\"\n (click)=\"$event.stopPropagation()\"\n *dropdownMenu\n >\n <formly-form\n class=\"p-16 d-block\"\n style=\"min-width: 250px\"\n [form]=\"column.filteringConfig.formGroup\"\n [fields]=\"column.filteringConfig.fields\"\n [model]=\"column.filteringConfig.model\"\n ></formly-form>\n <div class=\"p-16 fit-w d-flex separator-top\">\n <button\n class=\"btn btn-default btn-sm m-r-8 grow\"\n title=\"{{ 'Reset' | translate }}\"\n (click)=\"resetFilter(column, gridHeaderDropdown)\"\n translate\n >\n Reset\n </button>\n <button\n class=\"btn btn-primary btn-sm grow\"\n title=\"{{ 'Apply' | translate }}\"\n (click)=\"applyFilter(column, gridHeaderDropdown)\"\n translate\n >\n Apply\n </button>\n </div>\n </div>\n </div>\n </div>\n }\n <!-- Sort button -->\n <div class=\"d-flex a-i-center gap-4 flex-no-shrink\">\n <button\n class=\"btn btn-icon btn-default btn-xs\"\n [attr.aria-label]=\"'Sort order' | translate\"\n tooltip=\"{{ 'Sort order' | translate }}\"\n placement=\"top\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"changeSortOrder(column)\"\n >\n @if (column.sortOrder === 'asc') {\n <i c8yIcon=\"long-arrow-up\"></i>\n } @else if (column.sortOrder === 'desc') {\n <i c8yIcon=\"long-arrow-down\"></i>\n } @else {\n <i\n c8yIcon=\"exchange\"\n style=\"transform: rotate(90deg)\"\n ></i>\n }\n </button>\n </div>\n <!-- Column type dropdown -->\n @if (!column.isOperation && column.__origin !== 'status') {\n <div\n class=\"d-flex a-i-center gap-4 flex-no-shrink\"\n [ngClass]=\"{ 'no-pointer invisible': column.__origin === 'status' }\"\n >\n <div\n class=\"dropdown d-flex\"\n container=\"body\"\n dropdown\n c8yDropdownFocusTrap\n #columnTypeDropdown=\"bs-dropdown\"\n >\n <button\n class=\"btn btn-default btn-xs dropdown-toggle c8y-dropdown\"\n [attr.aria-label]=\"'Select render type' | translate\"\n tooltip=\"{{ 'Render type' | translate }}\"\n placement=\"top\"\n container=\"body\"\n [attr.aria-haspopup]=\"true\"\n [attr.aria-expanded]=\"columnTypeDropdown.isOpen\"\n type=\"button\"\n [delay]=\"500\"\n dropdownToggle\n >\n {{ column.type | translate | humanize }}\n <i c8yIcon=\"caret-down\"></i>\n </button>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n >\n <c8y-list-group role=\"list\">\n @if (column.type === 'computed' || column.computedConfig) {\n <c8y-li\n tabindex=\"0\"\n role=\"listitem\"\n (keydown.enter)=\"\n applyColumnType('computed', column); $event.preventDefault()\n \"\n (keydown.space)=\"\n applyColumnType('computed', column); $event.preventDefault()\n \"\n (click)=\"applyColumnType('computed', column)\"\n [active]=\"column.type === 'computed'\"\n >\n {{ 'Computed' | translate }}\n </c8y-li>\n <c8y-li\n tabindex=\"0\"\n role=\"listitem\"\n (click)=\"applyColumnType('icon', column)\"\n (keydown.enter)=\"\n applyColumnType('icon', column); $event.preventDefault()\n \"\n (keydown.space)=\"\n applyColumnType('icon', column); $event.preventDefault()\n \"\n [active]=\"column.type === 'icon'\"\n >\n {{ 'Icon' | translate }}\n </c8y-li>\n } @else {\n <c8y-li\n tabindex=\"0\"\n role=\"listitem\"\n (click)=\"applyColumnType('default', column)\"\n (keydown.enter)=\"\n applyColumnType('default', column); $event.preventDefault()\n \"\n (keydown.space)=\"\n applyColumnType('default', column); $event.preventDefault()\n \"\n [active]=\"column.type === 'default'\"\n >\n {{ 'Default' | translate }}\n </c8y-li>\n <c8y-li\n tabindex=\"0\"\n role=\"listitem\"\n (click)=\"applyColumnType('date', column)\"\n (keydown.enter)=\"\n applyColumnType('date', column); $event.preventDefault()\n \"\n (keydown.space)=\"\n applyColumnType('date', column); $event.preventDefault()\n \"\n [active]=\"column.type === 'date'\"\n >\n {{ 'Date' | translate }}\n </c8y-li>\n <c8y-li\n tabindex=\"0\"\n role=\"listitem\"\n (click)=\"applyColumnType('icon', column)\"\n [active]=\"column.type === 'icon'\"\n (keydown.enter)=\"\n applyColumnType('icon', column); $event.preventDefault()\n \"\n (keydown.space)=\"\n applyColumnType('icon', column); $event.preventDefault()\n \"\n >\n {{ 'Icon' | translate }}\n </c8y-li>\n }\n </c8y-list-group>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n @if (column.isOperation) {\n <ng-container>\n @if (true) {\n <c8y-li-action\n icon=\"edit1\"\n [attr.aria-label]=\"'Edit' | translate\"\n label=\"Edit\"\n (click)=\"editOperationColumn(column)\"\n ></c8y-li-action>\n }\n @if (true) {\n <c8y-li-action\n icon=\"delete\"\n [attr.aria-label]=\"'Delete' | translate\"\n label=\"Delete\"\n (click)=\"deleteColumn(column)\"\n ></c8y-li-action>\n }\n </ng-container>\n }\n </c8y-li>\n }\n }\n @if (columns.length === 0) {\n <c8y-ui-empty-state\n [icon]=\"'list'\"\n [title]=\"'No properties selected.' | translate\"\n [subtitle]=\"'Select from the list which columns to display.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n }\n</c8y-list-group>\n<div class=\"m-b-16 p-t-16 p-l-24 p-r-16 fit-w separator-top\">\n <div\n class=\"dropdown\"\n container=\"body\"\n dropdown\n [insideClick]=\"true\"\n c8yDropdownFocusTrap\n #customActionsDropdown=\"bs-dropdown\"\n >\n <button\n class=\"btn btn-default btn-sm dropdown-toggle c8y-dropdown\"\n [title]=\"'Add action' | translate\"\n [attr.aria-haspopup]=\"true\"\n [attr.aria-expanded]=\"customActionsDropdown.isOpen\"\n type=\"button\"\n dropdownToggle\n >\n <i c8yIcon=\"cog\"></i>\n <span>{{ 'Add action' | translate }}</span>\n <span class=\"caret\"></span>\n </button>\n <ul\n class=\"dropdown-menu\"\n *dropdownMenu\n >\n <li>\n <button\n class=\"dropdown-item\"\n type=\"button\"\n (click)=\"onCreateOperation()\"\n >\n {{ 'Create operation' | translate }}\n </button>\n </li>\n <li>\n <button\n class=\"dropdown-item\"\n type=\"button\"\n (click)=\"onToggleMaintenanceMode()\"\n [disabled]=\"hasMaintenanceColumn\"\n >\n {{ 'Toggle maintenance mode' | translate }}\n </button>\n </li>\n </ul>\n </div>\n</div>\n" }]
1909
+ }] });
1910
+
1911
+ class AssetTablePropertiesSelectorComponent {
1912
+ constructor() {
1913
+ this.widgetConfigService = inject(WidgetConfigService);
1914
+ this.selectedProperties = [];
1915
+ this.extraProperties = [];
1916
+ this.inventoryService = inject(InventoryService);
1917
+ }
1918
+ async ngOnInit() {
1919
+ this.asset = (await this.inventoryService.detail(this.widgetConfigService.currentConfig.device?.id)).data;
1920
+ this.selectedProperties = this.widgetConfigService.currentConfig?.selectedProperties || [];
1921
+ this.extraProperties = this.selectedProperties.filter(prop => prop?.computed);
1922
+ }
1923
+ selectProperties(selectedProperties) {
1924
+ // if the selected properties name matches and the existing one has iconConfig, retain it
1925
+ const updatedProperties = selectedProperties.map(prop => {
1926
+ const existingProp = this.selectedProperties.find(p => p.name === prop.name);
1927
+ if (existingProp?.iconConfig || existingProp?.columnType) {
1928
+ return {
1929
+ ...prop,
1930
+ iconConfig: existingProp.iconConfig,
1931
+ columnType: existingProp.columnType
1932
+ };
1933
+ }
1934
+ return prop;
1935
+ });
1936
+ this.widgetConfigService.updateConfig({ selectedProperties: updatedProperties });
1937
+ }
1938
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTablePropertiesSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1939
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: AssetTablePropertiesSelectorComponent, isStandalone: true, selector: "c8y-asset-table-properties-selector", host: { classAttribute: "bg-level-1" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset bg-level-1 p-0 overflow-hidden m-t-0\">\n <legend class=\"m-l-16\">{{ 'Select properties' | translate }}</legend>\n <c8y-asset-property-list\n class=\"inner-scroll bg-inherit d-block\"\n style=\"max-height: 300px\"\n [asset]=\"asset\"\n [config]=\"{\n selectMode: 'multi',\n expansionMode: 'expandedByDefault',\n allowAddingCustomProperties: true,\n showValue: false,\n showKey: false,\n selectedProperties: selectedProperties\n }\"\n [extraProperties]=\"extraProperties\"\n (selectedProperties)=\"selectProperties($event)\"\n ></c8y-asset-property-list>\n</fieldset>\n<fieldset class=\"c8y-fieldset bg-level-1 overflow-hidden p-0\">\n <legend class=\"m-l-16\">{{ 'Properties column settings (drag to reorder)' | translate }}</legend>\n <c8y-asset-table-column-settings></c8y-asset-table-column-settings>\n</fieldset>\n", dependencies: [{ kind: "component", type: AssetPropertyListComponent, selector: "c8y-asset-property-list", inputs: ["config", "asset", "extraProperties"], outputs: ["selectedProperties"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "component", type: AssetTableGridSettingsComponent, selector: "c8y-asset-table-column-settings" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
1940
+ }
1941
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetTablePropertiesSelectorComponent, decorators: [{
1942
+ type: Component,
1943
+ args: [{ selector: 'c8y-asset-table-properties-selector', host: {
1944
+ class: 'bg-level-1'
1945
+ }, standalone: true, imports: [
1946
+ AssetPropertyListComponent,
1947
+ PopoverModule,
1948
+ TooltipModule,
1949
+ AssetTableGridSettingsComponent,
1950
+ C8yTranslatePipe
1951
+ ], template: "<fieldset class=\"c8y-fieldset bg-level-1 p-0 overflow-hidden m-t-0\">\n <legend class=\"m-l-16\">{{ 'Select properties' | translate }}</legend>\n <c8y-asset-property-list\n class=\"inner-scroll bg-inherit d-block\"\n style=\"max-height: 300px\"\n [asset]=\"asset\"\n [config]=\"{\n selectMode: 'multi',\n expansionMode: 'expandedByDefault',\n allowAddingCustomProperties: true,\n showValue: false,\n showKey: false,\n selectedProperties: selectedProperties\n }\"\n [extraProperties]=\"extraProperties\"\n (selectedProperties)=\"selectProperties($event)\"\n ></c8y-asset-property-list>\n</fieldset>\n<fieldset class=\"c8y-fieldset bg-level-1 overflow-hidden p-0\">\n <legend class=\"m-l-16\">{{ 'Properties column settings (drag to reorder)' | translate }}</legend>\n <c8y-asset-table-column-settings></c8y-asset-table-column-settings>\n</fieldset>\n" }]
1952
+ }] });
1953
+
1954
+ /**
1955
+ * Generated bundle index. Do not edit.
1956
+ */
1957
+
1958
+ export { AssetTableGridSettingsComponent, AssetTablePropertiesSelectorComponent, AssetTableWidgetConfigComponent, AssetTableWidgetViewComponent };
1959
+ //# sourceMappingURL=c8y-ngx-components-widgets-implementations-asset-table.mjs.map