@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.
- package/asset-properties/index.d.ts +1 -2
- package/asset-properties/index.d.ts.map +1 -1
- package/context-dashboard/index.d.ts.map +1 -1
- package/datapoint-explorer/view/index.d.ts +8 -0
- package/datapoint-explorer/view/index.d.ts.map +1 -1
- package/datapoint-selector/index.d.ts +14 -1
- package/datapoint-selector/index.d.ts.map +1 -1
- package/echart/index.d.ts +7 -2
- package/echart/index.d.ts.map +1 -1
- package/echart/models/index.d.ts +1 -0
- package/echart/models/index.d.ts.map +1 -1
- package/fesm2022/c8y-ngx-components-asset-properties.mjs +3 -4
- package/fesm2022/c8y-ngx-components-asset-properties.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs +2 -3
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs +33 -4
- package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-datapoint-selector.mjs +24 -7
- package/fesm2022/c8y-ngx-components-datapoint-selector.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-device-grid.mjs +1 -1
- package/fesm2022/c8y-ngx-components-device-grid.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-echart-models.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-echart.mjs +128 -52
- package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-global-context.mjs +7 -2
- package/fesm2022/c8y-ngx-components-global-context.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions-asset-table.mjs +127 -0
- package/fesm2022/c8y-ngx-components-widgets-definitions-asset-table.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-widgets-definitions-datapoints-graph.mjs +3 -3
- package/fesm2022/c8y-ngx-components-widgets-definitions-datapoints-graph.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions.mjs +1 -0
- package/fesm2022/c8y-ngx-components-widgets-definitions.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-exports.mjs +8 -1
- package/fesm2022/c8y-ngx-components-widgets-exports.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs +1959 -0
- package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs +90 -56
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components.mjs +1724 -1464
- package/fesm2022/c8y-ngx-components.mjs.map +1 -1
- package/global-context/index.d.ts +1 -0
- package/global-context/index.d.ts.map +1 -1
- package/index.d.ts +1453 -1339
- package/index.d.ts.map +1 -1
- package/locales/de.po +213 -3
- package/locales/es.po +213 -3
- package/locales/fr.po +213 -3
- package/locales/ja_JP.po +213 -3
- package/locales/ko.po +213 -3
- package/locales/locales.pot +219 -3
- package/locales/nl.po +213 -3
- package/locales/pl.po +213 -3
- package/locales/pt_BR.po +213 -3
- package/locales/zh_CN.po +213 -3
- package/locales/zh_TW.po +213 -3
- package/package.json +1 -1
- package/widgets/cockpit-exports/index.d.ts +6 -0
- package/widgets/cockpit-exports/index.d.ts.map +1 -1
- package/widgets/definitions/asset-table/index.d.ts +6 -0
- package/widgets/definitions/asset-table/index.d.ts.map +1 -0
- package/widgets/definitions/index.d.ts +1 -0
- package/widgets/definitions/index.d.ts.map +1 -1
- package/widgets/device-management-exports/index.d.ts +6 -0
- package/widgets/device-management-exports/index.d.ts.map +1 -1
- package/widgets/exports/index.d.ts +8 -1
- package/widgets/exports/index.d.ts.map +1 -1
- package/widgets/implementations/asset-table/index.d.ts +229 -0
- package/widgets/implementations/asset-table/index.d.ts.map +1 -0
- package/widgets/implementations/datapoints-graph/index.d.ts +14 -3
- 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
|