@c8y/tutorial 1021.80.1 → 1021.81.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.
@@ -415,6 +415,13 @@ export default {
415
415
  description: 'An example for a server data grid.',
416
416
  scope: 'self'
417
417
  },
418
+ {
419
+ name: 'Tree grid example',
420
+ module: 'TreeGridExampleModule',
421
+ path: './src/grids/tree-grid-example/tree-grid-example.module.ts',
422
+ description: 'An example for a tree data grid.',
423
+ scope: 'self'
424
+ },
418
425
  {
419
426
  name: 'Device grid example',
420
427
  module: 'DeviceGridExampleModule',
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@c8y/tutorial",
3
- "version": "1021.80.1",
3
+ "version": "1021.81.0",
4
4
  "description": "This package is used to scaffold a tutorial for Cumulocity IoT Web SDK which explains a lot of concepts.",
5
5
  "dependencies": {
6
- "@c8y/style": "1021.80.1",
7
- "@c8y/ngx-components": "1021.80.1",
8
- "@c8y/client": "1021.80.1",
9
- "@c8y/bootstrap": "1021.80.1",
6
+ "@c8y/style": "1021.81.0",
7
+ "@c8y/ngx-components": "1021.81.0",
8
+ "@c8y/client": "1021.81.0",
9
+ "@c8y/bootstrap": "1021.81.0",
10
10
  "@angular/cdk": "^18.2.14",
11
11
  "ngx-bootstrap": "18.0.0",
12
12
  "leaflet": "1.9.4",
13
13
  "rxjs": "^7.8.1"
14
14
  },
15
15
  "devDependencies": {
16
- "@c8y/options": "1021.80.1",
17
- "@c8y/devkit": "1021.80.1"
16
+ "@c8y/options": "1021.81.0",
17
+ "@c8y/devkit": "1021.81.0"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@angular/common": ">=18 <19"
@@ -1,12 +1,14 @@
1
1
  <c8y-title>Data grid examples</c8y-title>
2
2
 
3
3
  <c8y-data-grid
4
+ class="content-fullpage d-flex d-col border-top border-bottom"
4
5
  [title]="title"
5
6
  [loadMoreItemsLabel]="loadMoreItemsLabel"
6
7
  [loadingItemsLabel]="loadingItemsLabel"
7
8
  [displayOptions]="displayOptions"
8
9
  [columns]="columns"
9
10
  [pagination]="pagination"
11
+ [childNodePagination]="childNodePagination"
10
12
  [infiniteScroll]="infiniteScroll"
11
13
  [serverSideDataCallback]="serverSideDataCallback"
12
14
  [refresh]="refresh"
@@ -16,7 +18,6 @@
16
18
  (itemsSelect)="onItemsSelect($event)"
17
19
  [bulkActionControls]="bulkActionControls"
18
20
  (onConfigChange)="onConfigChange($event)"
19
- class="content-fullpage d-flex d-col border-top border-bottom"
20
21
  >
21
22
  <c8y-ui-empty-state
22
23
  [icon]="'search'"
@@ -0,0 +1,18 @@
1
+ <c8y-title>Data grid examples</c8y-title>
2
+
3
+ <c8y-data-grid
4
+ class="content-fullpage d-flex d-col border-top border-bottom"
5
+ [title]="title"
6
+ [columns]="columns"
7
+ [rows]="data"
8
+ [treeGrid]="true"
9
+ [pagination]="pagination"
10
+ [selectable]="selectable"
11
+ (itemsSelect)="onItemsSelect($event)"
12
+ [actionControls]="actionControls"
13
+ [bulkActionControls]="bulkActionControls"
14
+ (onConfigChange)="onConfigChange($event)"
15
+ (onFilter)="onFilter($event)"
16
+ (onAddCustomColumn)="onAddCustomColumn($event)"
17
+ (onRemoveCustomColumn)="onRemoveCustomColumn($event)"
18
+ ></c8y-data-grid>
@@ -0,0 +1,169 @@
1
+ import { Component, OnInit } from '@angular/core';
2
+ import { RouterModule } from '@angular/router';
3
+ import {
4
+ ActionControl,
5
+ BuiltInActionType,
6
+ BulkActionControl,
7
+ Column,
8
+ CoreModule,
9
+ CustomColumnConfig,
10
+ GridConfig,
11
+ Pagination,
12
+ Row
13
+ } from '@c8y/ngx-components';
14
+ import { DeviceGridModule } from '@c8y/ngx-components/device-grid';
15
+ import { data } from './client-tree-grid.data';
16
+
17
+ /**
18
+ * This is an example of using DataGridComponent to display a static set of data
19
+ * and allow user for filering and sorting it on client side.
20
+ */
21
+ @Component({
22
+ selector: 'c8y-client-tree-grid-example',
23
+ templateUrl: './client-tree-grid-example.component.html',
24
+ standalone: true,
25
+ imports: [CoreModule, DeviceGridModule, RouterModule]
26
+ })
27
+ export class ClientGridExampleComponent implements OnInit {
28
+ /** This will be used as a title for the data grid. */
29
+ title = 'Devices';
30
+ /**
31
+ * This defines what columns will be displayed in the grid.
32
+ * In this example we're just displaying properties from the items from the loaded data file.
33
+ */
34
+ columns: Column[] = [
35
+ {
36
+ name: 'name',
37
+ header: 'Name',
38
+ path: 'name',
39
+ filterable: true
40
+ },
41
+ {
42
+ name: 'id',
43
+ header: 'ID',
44
+ path: 'id',
45
+ filterable: true
46
+ },
47
+ {
48
+ name: 'serialNumber',
49
+ header: 'Serial number',
50
+ path: 'serialNumber',
51
+ filterable: true
52
+ },
53
+ {
54
+ name: 'firmwareVersion',
55
+ header: 'Firmware',
56
+ path: 'firmwareVersion',
57
+ filterable: true
58
+ }
59
+ ];
60
+ /** Initial pagination settings. */
61
+ pagination: Pagination = {
62
+ pageSize: 30,
63
+ currentPage: 1
64
+ };
65
+ /** Will allow for selecting items and perform bulk actions on them. */
66
+ selectable = true;
67
+ /**
68
+ * Defines actions for individual rows.
69
+ * `type` can be one of the predefined ones, or a custom one.
70
+ * `callback` executes the action (based on the selected item object).
71
+ */
72
+ actionControls: ActionControl[] = [
73
+ {
74
+ type: BuiltInActionType.Edit,
75
+ callback: selectedItem => this.onItemEdit(selectedItem)
76
+ },
77
+ {
78
+ type: BuiltInActionType.Delete,
79
+ callback: selectedItem => this.onItemDelete(selectedItem)
80
+ }
81
+ ];
82
+ /**
83
+ * Defines actions for multiple rows.
84
+ * `type` can be one of the predefined ones, or a custom one.
85
+ * `callback` executes the action (based on the ids of selected items).
86
+ */
87
+ bulkActionControls: BulkActionControl[] = [
88
+ {
89
+ type: BuiltInActionType.Export,
90
+ callback: selectedItemIds => this.onItemsExport(selectedItemIds)
91
+ },
92
+ {
93
+ type: BuiltInActionType.Delete,
94
+ callback: selectedItemIds => this.onItemsDelete(selectedItemIds)
95
+ }
96
+ ];
97
+ /** Static data to display. */
98
+ data: any[];
99
+
100
+ ngOnInit() {
101
+ // load static data from another file, could be loaded from anywhere
102
+ this.data = this.dataToRows(data);
103
+ console.log('data loaded:', this.data);
104
+ }
105
+
106
+ /** Executes an edit action on the selected item. */
107
+ onItemEdit(selectedItem) {
108
+ console.log('item to edit:');
109
+ console.dir(selectedItem);
110
+ }
111
+
112
+ /** Executes a delete action on the selected item. */
113
+ onItemDelete(selectedItem) {
114
+ console.log('item to delete:');
115
+ console.dir(selectedItem);
116
+ }
117
+
118
+ /** Executes an action on selected items, whenever the selection changes. */
119
+ onItemsSelect(selectedItemIds) {
120
+ console.log('selected items:');
121
+ console.dir(selectedItemIds);
122
+ }
123
+
124
+ /** Executes an export action of the selected multiple items. */
125
+ onItemsExport(selectedItemIds) {
126
+ console.log('items to export:');
127
+ console.dir(selectedItemIds);
128
+ }
129
+
130
+ /** Executes a delete action on the selected multiple items. */
131
+ onItemsDelete(selectedItemIds) {
132
+ console.log('items to delete:');
133
+ console.dir(selectedItemIds);
134
+ }
135
+
136
+ /** Executes logic when data grid config changes. */
137
+ onConfigChange(config: GridConfig) {
138
+ console.log('configuration changed:');
139
+ console.dir(config);
140
+ }
141
+
142
+ onFilter(filter) {
143
+ console.log('filter changed:');
144
+ console.log(filter);
145
+ }
146
+
147
+ onAddCustomColumn(customColumnConfig: CustomColumnConfig) {
148
+ console.log('custom column added:');
149
+ console.log(customColumnConfig);
150
+ }
151
+
152
+ onRemoveCustomColumn(column: Column) {
153
+ console.log('custom column removed:');
154
+ console.log(column);
155
+ }
156
+
157
+ private dataToRows(data: any[], parentRow?: Row): Row[] {
158
+ return data.map(item => {
159
+ const row = {
160
+ ...item,
161
+ hasChildren: item.childNodes?.length > 0,
162
+ parentRow
163
+ };
164
+ row.childNodes =
165
+ item.childNodes?.length > 0 ? this.dataToRows(item.childNodes, row) : undefined;
166
+ return row;
167
+ });
168
+ }
169
+ }
@@ -0,0 +1,147 @@
1
+ /** Just a sample set of data. */
2
+ export const data = [
3
+ {
4
+ id: '1000000001',
5
+ name: 'SmartTemp XT-200 Environmental Sensor',
6
+ serialNumber: 'SN-XT200-9F4G7K',
7
+ firmwareVersion: '1.3.2',
8
+ childNodes: [
9
+ {
10
+ id: '1000000101',
11
+ name: 'Temperature Probe',
12
+ serialNumber: 'CH-TEMP-234XF',
13
+ firmwareVersion: '2.1.0',
14
+ childNodes: [
15
+ {
16
+ id: '1000000111',
17
+ name: 'Thermistor Core',
18
+ serialNumber: 'CMP-TC-0021',
19
+ firmwareVersion: '1.0.5'
20
+ },
21
+ {
22
+ id: '1000000112',
23
+ name: 'Shielding Module',
24
+ serialNumber: 'CMP-SH-3992',
25
+ firmwareVersion: '1.1.1'
26
+ }
27
+ ]
28
+ },
29
+ {
30
+ id: '1000000102',
31
+ name: 'Humidity Sensor',
32
+ serialNumber: 'CH-HMD-92JF3',
33
+ firmwareVersion: '2.0.3',
34
+ childNodes: [
35
+ {
36
+ id: '1000000121',
37
+ name: 'Moisture Grid',
38
+ serialNumber: 'CMP-MG-1200',
39
+ firmwareVersion: '1.0.2'
40
+ },
41
+ {
42
+ id: '1000000122',
43
+ name: 'Condensation Shield',
44
+ serialNumber: 'CMP-CS-8902',
45
+ firmwareVersion: '1.0.7'
46
+ }
47
+ ]
48
+ }
49
+ ]
50
+ },
51
+ {
52
+ id: '1000000002',
53
+ name: 'AquaSense Flow Meter V3',
54
+ serialNumber: 'SN-AQ3-V3J8L2',
55
+ firmwareVersion: '4.0.1',
56
+ childNodes: [
57
+ {
58
+ id: '1000000201',
59
+ name: 'Flow Rate Sensor',
60
+ serialNumber: 'CH-FLOW-12LD9',
61
+ firmwareVersion: '3.2.0',
62
+ childNodes: [
63
+ {
64
+ id: '1000000211',
65
+ name: 'Turbine Core',
66
+ serialNumber: 'CMP-TUR-3456',
67
+ firmwareVersion: '1.1.3'
68
+ },
69
+ {
70
+ id: '1000000212',
71
+ name: 'Velocity Encoder',
72
+ serialNumber: 'CMP-VEL-0082',
73
+ firmwareVersion: '1.0.9'
74
+ }
75
+ ]
76
+ },
77
+ {
78
+ id: '1000000202',
79
+ name: 'Pressure Gauge',
80
+ serialNumber: 'CH-PRES-77XMN',
81
+ firmwareVersion: '3.1.1',
82
+ childNodes: [
83
+ {
84
+ id: '1000000221',
85
+ name: 'Sensor Diaphragm',
86
+ serialNumber: 'CMP-DIA-4712',
87
+ firmwareVersion: '1.2.5'
88
+ },
89
+ {
90
+ id: '1000000222',
91
+ name: 'Analog-to-Digital Module',
92
+ serialNumber: 'CMP-ADM-9913',
93
+ firmwareVersion: '1.3.0'
94
+ }
95
+ ]
96
+ }
97
+ ]
98
+ },
99
+ {
100
+ id: '1000000003',
101
+ name: 'SkyLink Drone Beacon',
102
+ serialNumber: 'SN-SKYB-239DKT',
103
+ firmwareVersion: '2.5.0',
104
+ childNodes: [
105
+ {
106
+ id: '1000000301',
107
+ name: 'GPS Module',
108
+ serialNumber: 'CH-GPS-0039H',
109
+ firmwareVersion: '1.9.0',
110
+ childNodes: [
111
+ {
112
+ id: '1000000311',
113
+ name: 'Antenna Array',
114
+ serialNumber: 'CMP-ANT-2100',
115
+ firmwareVersion: '1.0.4'
116
+ },
117
+ {
118
+ id: '1000000312',
119
+ name: 'Triangulation Processor',
120
+ serialNumber: 'CMP-TRI-3029',
121
+ firmwareVersion: '1.1.2'
122
+ }
123
+ ]
124
+ },
125
+ {
126
+ id: '1000000302',
127
+ name: 'Altitude Sensor',
128
+ serialNumber: 'CH-ALT-112PK',
129
+ firmwareVersion: '2.4.3',
130
+ childNodes: [
131
+ {
132
+ id: '1000000321',
133
+ name: 'Barometric Core',
134
+ serialNumber: 'CMP-BAR-0192',
135
+ firmwareVersion: '1.0.8'
136
+ },
137
+ {
138
+ id: '1000000322',
139
+ name: 'Drift Compensator',
140
+ serialNumber: 'CMP-DRF-6674',
141
+ firmwareVersion: '1.2.1'
142
+ }
143
+ ]
144
+ }
145
+ ]
146
+ }
147
+ ];
@@ -0,0 +1,30 @@
1
+ <c8y-title>Data grid examples</c8y-title>
2
+
3
+ <c8y-data-grid
4
+ class="content-fullpage d-flex d-col border-top border-bottom"
5
+ [title]="title"
6
+ [loadMoreItemsLabel]="loadMoreItemsLabel"
7
+ [loadingItemsLabel]="loadingItemsLabel"
8
+ [displayOptions]="displayOptions"
9
+ [columns]="columns"
10
+ [pagination]="pagination"
11
+ [childNodePagination]="childNodePagination"
12
+ [infiniteScroll]="infiniteScroll"
13
+ [treeGrid]="true"
14
+ [serverSideDataCallback]="serverSideDataCallback"
15
+ [refresh]="refresh"
16
+ (rowClick)="onRowClick($event)"
17
+ [actionControls]="actionControls"
18
+ [selectable]="selectable"
19
+ parentNodeLabelProperty="name"
20
+ (itemsSelect)="onItemsSelect($event)"
21
+ [bulkActionControls]="bulkActionControls"
22
+ (onConfigChange)="onConfigChange($event)"
23
+ >
24
+ <c8y-ui-empty-state
25
+ [icon]="'search'"
26
+ [title]="'No results to display.' | translate"
27
+ [subtitle]="'Refine your search terms or check your spelling.' | translate"
28
+ [horizontal]="true"
29
+ ></c8y-ui-empty-state>
30
+ </c8y-data-grid>
@@ -0,0 +1,156 @@
1
+ import { Component, EventEmitter } from '@angular/core';
2
+ import { RouterModule } from '@angular/router';
3
+
4
+ import {
5
+ ActionControl,
6
+ BulkActionControl,
7
+ Column,
8
+ CoreModule,
9
+ DataSourceModifier,
10
+ DisplayOptions,
11
+ GridConfig,
12
+ GridConfigContext,
13
+ GridConfigContextProvider,
14
+ LoadMoreMode,
15
+ Pagination,
16
+ Row,
17
+ ServerSideDataResult
18
+ } from '@c8y/ngx-components';
19
+ import { DeviceGridModule } from '@c8y/ngx-components/device-grid';
20
+ import { ServerTreeGridExampleService } from './server-tree-grid-example.service';
21
+
22
+ /**
23
+ * This is an example of using DataGridComponent for displaying, filtering and sorting managed objects
24
+ * using customized columns and dynamically built inventory queries.
25
+ */
26
+ @Component({
27
+ selector: 'c8y-server-tree-grid-example',
28
+ templateUrl: './server-tree-grid-example.component.html',
29
+ standalone: true,
30
+ imports: [CoreModule, DeviceGridModule, RouterModule],
31
+ providers: [ServerTreeGridExampleService]
32
+ })
33
+ export class ServerTreeGridExampleComponent implements GridConfigContextProvider {
34
+ title = 'Managed objects';
35
+ loadMoreItemsLabel = 'Load more managed objects';
36
+ loadingItemsLabel = 'Loading managed objects…';
37
+
38
+ displayOptions: DisplayOptions = {
39
+ bordered: true,
40
+ striped: true,
41
+ filter: true,
42
+ gridHeader: true,
43
+ hover: true
44
+ };
45
+
46
+ columns: Column[];
47
+ pagination: Pagination;
48
+ childNodePagination: Pagination;
49
+ infiniteScroll: LoadMoreMode = 'auto';
50
+ serverSideDataCallback: any;
51
+
52
+ refresh: EventEmitter<any> = new EventEmitter<any>();
53
+
54
+ selectable = true;
55
+ actionControls: ActionControl[];
56
+ bulkActionControls: BulkActionControl[];
57
+
58
+ constructor(private service: ServerTreeGridExampleService) {
59
+ this.columns = this.service.getColumns();
60
+ this.pagination = this.service.getPagination();
61
+ this.childNodePagination = this.service.getChildNodePagination();
62
+ this.actionControls = this.service.getActionControls();
63
+ this.bulkActionControls = this.service.getBulkActionControls();
64
+ // we're setting up `serverSideDataCallback` to execute a method from this component with bound `this`
65
+ this.serverSideDataCallback = this.onDataSourceModifier.bind(this);
66
+ // we're setting up `onRefreshClick` to be executed on refresh event
67
+ this.refresh.subscribe(() => this.onRefreshClick());
68
+ }
69
+
70
+ getGridConfigContext(): GridConfigContext {
71
+ return {
72
+ /**
73
+ * You can provide data here that can be used for grid configration storage,
74
+ * action control matchers, etc.
75
+ */
76
+ key: 'server-grid-example'
77
+ };
78
+ }
79
+
80
+ /** Used in ngFor for columns iteration. */
81
+ trackByName(_index, column: Column): string {
82
+ return column.name;
83
+ }
84
+
85
+ /**
86
+ * This method loads data when data grid requests it (e.g. on initial load or on column settings change).
87
+ * It gets the object with current data grid setup and is supposed to return:
88
+ * full response, list of items, paging object, the number of items in the filtered subset, the number of all items.
89
+ */
90
+ async onDataSourceModifier(
91
+ dataSourceModifier: DataSourceModifier
92
+ ): Promise<ServerSideDataResult> {
93
+ const { parentRow } = dataSourceModifier;
94
+ if (parentRow) {
95
+ const { res, data, paging } = await this.service.getChildDevices(
96
+ parentRow?.id,
97
+ parentRow?.pagination
98
+ );
99
+
100
+ data.forEach(row => {
101
+ row.hasChildren = row.childDevices.count > 0;
102
+ });
103
+
104
+ const serverSideDataResult: ServerSideDataResult = {
105
+ res,
106
+ data,
107
+ paging,
108
+ filteredSize: parentRow.childDevices.count,
109
+ size: parentRow.childDevices.count,
110
+ parentRow
111
+ };
112
+ return serverSideDataResult;
113
+ } else {
114
+ const { res, data, paging } = await this.service.getData(
115
+ dataSourceModifier.columns,
116
+ dataSourceModifier.pagination
117
+ );
118
+
119
+ data.forEach(row => {
120
+ row.hasChildren = row.childDevices.count > 0;
121
+ });
122
+
123
+ const filteredSize: number = await this.service.getCount(
124
+ dataSourceModifier.columns,
125
+ dataSourceModifier.pagination
126
+ );
127
+ const size: number = await this.service.getTotal();
128
+
129
+ const serverSideDataResult: ServerSideDataResult = { res, data, paging, filteredSize, size };
130
+ return serverSideDataResult;
131
+ }
132
+ }
133
+
134
+ /** Executes an action on row click. */
135
+ onRowClick(row: Row) {
136
+ console.log('row clicked:');
137
+ console.dir(row);
138
+ }
139
+
140
+ /** Executes an action on the selected items. */
141
+ onItemsSelect(selectedItemIds: string[]) {
142
+ console.log('selected item ids:');
143
+ console.dir(selectedItemIds);
144
+ }
145
+
146
+ /** Executes an action on grid config change. */
147
+ onConfigChange(gridConfig: GridConfig) {
148
+ console.log('grid config changed:');
149
+ console.dir(gridConfig);
150
+ }
151
+
152
+ /** Executes an action on refresh event. */
153
+ onRefreshClick() {
154
+ console.log('refresh clicked');
155
+ }
156
+ }
@@ -0,0 +1,255 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { transform } from 'lodash-es';
3
+
4
+ import { IManagedObject, InventoryService, QueriesUtil } from '@c8y/client';
5
+ import {
6
+ ActionControl,
7
+ BuiltInActionType,
8
+ BulkActionControl,
9
+ Column,
10
+ Pagination
11
+ } from '@c8y/ngx-components';
12
+
13
+ import { assign, get, identity } from 'lodash-es';
14
+
15
+ /** Model for custom type filtering form. */
16
+ export interface TypeFilteringModel {
17
+ group?: boolean;
18
+ device?: boolean;
19
+ smartRule?: boolean;
20
+ dashboard?: boolean;
21
+ file?: boolean;
22
+ application?: boolean;
23
+ }
24
+
25
+ /**
26
+ * This is the example service for a data grid:
27
+ * provides the list of columns, initial pagination object, actions;
28
+ * as well as performs the query for data based on the current grid setup.
29
+ */
30
+ @Injectable()
31
+ export class ServerTreeGridExampleService {
32
+ /** This will be used to build the inventory queries. */
33
+ protected queriesUtil: QueriesUtil;
34
+
35
+ constructor(protected inventoryService: InventoryService) {
36
+ this.queriesUtil = new QueriesUtil();
37
+ }
38
+
39
+ /**
40
+ * Returns a list of columns.
41
+ * We define 2 columns with inline objects (they display simple properties
42
+ * and use default header, cell and filtring form).
43
+ * The last column is defined via a class instance (it displays a value based on
44
+ * several properties of the row item and has custom header, cell and filtering form).
45
+ */
46
+ getColumns(): Column[] {
47
+ const columns = [
48
+ {
49
+ name: 'id',
50
+ header: 'ID',
51
+ path: 'id',
52
+ filterable: true,
53
+ sortable: true
54
+ },
55
+ {
56
+ name: 'name',
57
+ header: 'Name',
58
+ path: 'name',
59
+ filterable: true,
60
+ sortable: true
61
+ }
62
+ ];
63
+
64
+ return columns;
65
+ }
66
+
67
+ /** Returns initial pagination object. */
68
+ getPagination(): Pagination {
69
+ return {
70
+ pageSize: 10,
71
+ currentPage: 1
72
+ };
73
+ }
74
+
75
+ /** Returns initial child node pagination object. */
76
+ getChildNodePagination(): Pagination {
77
+ return {
78
+ pageSize: 2,
79
+ currentPage: 1
80
+ };
81
+ }
82
+
83
+ /** Returns an array of individual row actions. */
84
+ getActionControls(): ActionControl[] {
85
+ return [
86
+ { type: BuiltInActionType.Edit, callback: item => console.dir(item) },
87
+ { type: BuiltInActionType.Export, callback: item => console.dir(item) },
88
+ { type: BuiltInActionType.Delete, callback: item => console.dir(item) },
89
+ {
90
+ type: 'customAction',
91
+ icon: 'online',
92
+ text: 'Custom action',
93
+ callback: item => console.dir(item)
94
+ }
95
+ ];
96
+ }
97
+
98
+ /** Returns an array of bulk row actions. */
99
+ getBulkActionControls(): BulkActionControl[] {
100
+ return [
101
+ {
102
+ type: BuiltInActionType.Export,
103
+ callback: selectedItemIds => console.dir(selectedItemIds)
104
+ },
105
+ {
106
+ type: BuiltInActionType.Delete,
107
+ callback: selectedItemIds => console.dir(selectedItemIds)
108
+ },
109
+ {
110
+ type: 'customAction',
111
+ icon: 'online',
112
+ iconClasses: 'm-r-4',
113
+ text: 'Custom action',
114
+ showIf: selectedItemIds => selectedItemIds?.every(id => Number.parseInt(id) % 2 === 0),
115
+ callback: selectedItemIds => console.dir(selectedItemIds)
116
+ }
117
+ ];
118
+ }
119
+
120
+ /** Returns data for current columns and pagination setup. */
121
+ async getData(columns: Column[], pagination: Pagination) {
122
+ // build filters based on columns and pagination
123
+ const filters = this.getFilters(columns, pagination);
124
+ // execute inventory query for the list of managed objects
125
+ return this.inventoryService.list(filters);
126
+ }
127
+
128
+ /** Returns the number of items matching current columns and pagination setup. */
129
+ async getCount(columns: Column[], pagination: Pagination) {
130
+ const filters = {
131
+ ...this.getFilters(columns, pagination),
132
+ withTotalElements: true
133
+ };
134
+ return (await this.inventoryService.list(filters)).paging.totalElements;
135
+ }
136
+
137
+ /** Returns the total number of items (with no filters). */
138
+ async getTotal(): Promise<number> {
139
+ const filters = {
140
+ withTotalElements: true
141
+ };
142
+ return (await this.inventoryService.list(filters)).paging.totalElements;
143
+ }
144
+
145
+ async getChildDevices(parentId: string, pagination: Pagination) {
146
+ const filters = {
147
+ ...this.getFilters([], pagination),
148
+ withChildren: false,
149
+ withChildrenCount: true
150
+ };
151
+ return this.inventoryService.childDevicesList(parentId, filters);
152
+ }
153
+
154
+ /** Returns an icon and label representing the type of the managed object. */
155
+ getTypeIconAndLabel(mo: IManagedObject): { icon: string; label: string } {
156
+ let icon = 'question';
157
+ let label = 'Other';
158
+
159
+ if (mo.type === 'c8y_DeviceGroup') {
160
+ icon = 'c8y-group';
161
+ label = 'Group';
162
+ }
163
+
164
+ if (mo.c8y_IsDevice !== undefined) {
165
+ icon = 'exchange';
166
+ label = 'Device';
167
+ }
168
+
169
+ if (mo.type === 'c8y_SmartRule' || mo.type === 'c8y_PrivateSmartRule') {
170
+ icon = 'c8y-smart-rules';
171
+ label = 'Smart rule';
172
+ }
173
+
174
+ if (mo.c8y_Dashboard !== undefined) {
175
+ icon = 'th';
176
+ label = 'Dashboard';
177
+ }
178
+
179
+ if (mo.c8y_IsBinary !== undefined) {
180
+ icon = 'file';
181
+ label = 'File';
182
+ }
183
+
184
+ if (mo.type && mo.type.startsWith('c8y_Application')) {
185
+ icon = 'c8y-atom';
186
+ label = 'Application';
187
+ }
188
+
189
+ return { icon, label };
190
+ }
191
+
192
+ /** Returns filters for given columns and pagination setup. */
193
+ private getFilters(columns: Column[], pagination: Pagination) {
194
+ return {
195
+ query: this.getQueryString(columns),
196
+ pageSize: pagination.pageSize,
197
+ currentPage: pagination.currentPage,
198
+ withChildren: false,
199
+ withChildrenCount: true,
200
+ withTotalPages: true
201
+ };
202
+ }
203
+
204
+ /** Returns a query string based on columns setup. */
205
+ private getQueryString(columns: Column[]): string {
206
+ const fullQuery = this.getQueryObj(columns);
207
+ return this.queriesUtil.buildQuery(fullQuery);
208
+ }
209
+
210
+ /** Returns a query object based on columns setup. */
211
+ private getQueryObj(columns: Column[], defaultFilter = {}): any {
212
+ return transform(columns, (query, column) => this.addColumnQuery(query, column), {
213
+ __filter: {},
214
+ __orderby: [],
215
+ ...defaultFilter
216
+ });
217
+ }
218
+
219
+ /** Extends given query with a part based on the setup of given column. */
220
+ private addColumnQuery(query: any, column: Column): void {
221
+ // when a column is marked as filterable
222
+ if (column.filterable) {
223
+ // in the case of default filtering form, `filterPredicate` will contain the string entered by a user
224
+ if (column.filterPredicate) {
225
+ // so we use it as the expected value, * allow to search for it anywhere in the property
226
+ query.__filter[column.path] = `*${column.filterPredicate}*`;
227
+ }
228
+
229
+ // in the case of custom filtering form, we're storing the query in `externalFilterQuery.query`
230
+ if (column.externalFilterQuery) {
231
+ const getFilter = column.filteringConfig.getFilter || identity;
232
+ const queryObj = getFilter(column.externalFilterQuery);
233
+
234
+ if (queryObj.__or) {
235
+ query.__filter.__and = query.__filter.__and || [];
236
+ query.__filter.__and.push(queryObj);
237
+ } else if (queryObj.__and && get(query, '__filter.__and')) {
238
+ queryObj.__and.map(obj => query.__filter.__and.push(obj));
239
+ } else {
240
+ assign(query.__filter, queryObj);
241
+ }
242
+ }
243
+ }
244
+
245
+ // when a column is sortable and has a specified sorting order
246
+ if (column.sortable && column.sortOrder) {
247
+ // add sorting condition for the configured column `path`
248
+ query.__orderby.push({
249
+ [column.path]: column.sortOrder === 'asc' ? 1 : -1
250
+ });
251
+ }
252
+
253
+ return query;
254
+ }
255
+ }
@@ -0,0 +1,31 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { Router } from '@angular/router';
3
+ import { Tab, TabFactory } from '@c8y/ngx-components';
4
+
5
+ @Injectable()
6
+ export class TreeGridTabFactory implements TabFactory {
7
+ constructor(public router: Router) {}
8
+ get() {
9
+ const tabs: Tab[] = [];
10
+ if (this.router.url.match(/grids\/tree-grid-example/g)) {
11
+ tabs.push(
12
+ {
13
+ path: 'grids/tree-grid-example/server',
14
+ label: 'Server side data',
15
+ icon: 'server',
16
+ priority: 1050,
17
+ orientation: 'horizontal'
18
+ } as Tab,
19
+ {
20
+ path: 'grids/tree-grid-example/client',
21
+ label: 'Client side data',
22
+ icon: 'house',
23
+ priority: 1000,
24
+ orientation: 'horizontal'
25
+ } as Tab
26
+ );
27
+ }
28
+
29
+ return tabs;
30
+ }
31
+ }
@@ -0,0 +1,36 @@
1
+ import { NgModule } from '@angular/core';
2
+ import {
3
+ CommonModule,
4
+ hookNavigator,
5
+ hookRoute,
6
+ hookTab,
7
+ NavigatorNode
8
+ } from '@c8y/ngx-components';
9
+ import { TreeGridTabFactory } from './tree-grid-example-tab.factory';
10
+
11
+ @NgModule({
12
+ imports: [CommonModule],
13
+ providers: [
14
+ hookRoute({
15
+ path: 'grids/tree-grid-example/server',
16
+ loadComponent: () =>
17
+ import('./server-tree-grid-example.component').then(m => m.ServerTreeGridExampleComponent)
18
+ }),
19
+ hookRoute({
20
+ path: 'grids/tree-grid-example/client',
21
+ loadComponent: () =>
22
+ import('./client-tree-grid-example.component').then(m => m.ClientGridExampleComponent)
23
+ }),
24
+ hookNavigator(
25
+ new NavigatorNode({
26
+ priority: 20,
27
+ path: 'grids/tree-grid-example/server',
28
+ icon: 'tree-structure',
29
+ label: 'Tree grid',
30
+ parent: 'Data grid examples'
31
+ })
32
+ ),
33
+ hookTab(TreeGridTabFactory)
34
+ ]
35
+ })
36
+ export class TreeGridExampleModule {}