@c8y/tutorial 1021.2.1 → 1021.4.3

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/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@c8y/tutorial",
3
- "version": "1021.2.1",
3
+ "version": "1021.4.3",
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.2.1",
7
- "@c8y/ngx-components": "1021.2.1",
8
- "@c8y/client": "1021.2.1",
9
- "@c8y/bootstrap": "1021.2.1",
6
+ "@c8y/style": "1021.4.3",
7
+ "@c8y/ngx-components": "1021.4.3",
8
+ "@c8y/client": "1021.4.3",
9
+ "@c8y/bootstrap": "1021.4.3",
10
10
  "@angular/cdk": "^18.2.10",
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.2.1",
17
- "@c8y/devkit": "1021.2.1"
16
+ "@c8y/options": "1021.4.3",
17
+ "@c8y/devkit": "1021.4.3"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@angular/common": ">=18 <19"
@@ -0,0 +1,10 @@
1
+ import { Component } from '@angular/core';
2
+ import { CellRendererContext } from '@c8y/ngx-components';
3
+
4
+ @Component({
5
+ template: ` {{ context.value | c8yDate }} `,
6
+ selector: 'c8y-last-updated-cell-renderer'
7
+ })
8
+ export class LastUpdatedCellRendererComponent {
9
+ constructor(public context: CellRendererContext) {}
10
+ }
@@ -0,0 +1,100 @@
1
+ import { FormGroup } from '@angular/forms';
2
+ import { BaseColumn, ColumnConfig, gettext } from '@c8y/ngx-components';
3
+ import { LastUpdatedCellRendererComponent } from './last-updated.cell-renderer.component';
4
+
5
+ /**
6
+ * Defines a class for custom Last updated date column.
7
+ * Implements `Column` interface and sets basic properties, as well as custom components.
8
+ */
9
+ export class LastUpdatedDataGridColumn extends BaseColumn {
10
+ constructor(initialColumnConfig?: ColumnConfig) {
11
+ super(initialColumnConfig);
12
+ this.name = 'lastUpdated';
13
+ this.path = 'lastUpdated';
14
+ this.header = 'Last updated';
15
+
16
+ this.cellRendererComponent = LastUpdatedCellRendererComponent;
17
+
18
+ this.filterable = true;
19
+
20
+ this.filteringConfig = {
21
+ /**
22
+ * When using Formly schema, filter chips can be automatically generated by the
23
+ * data-grid library based on the filtering schema definition. You do not need
24
+ * to manually implement the chip generation. The Formly field definitions provided
25
+ * in the `filteringConfig` will automatically be transformed into chips by the
26
+ * data-grid system.
27
+ */
28
+ fields: [
29
+ {
30
+ type: 'object',
31
+ key: 'lastUpdated',
32
+ templateOptions: {
33
+ label: gettext('Show items last updated')
34
+ },
35
+ fieldGroup: [
36
+ {
37
+ type: 'date-time',
38
+ key: 'after',
39
+ templateOptions: {
40
+ label: gettext('from')
41
+ },
42
+ expressionProperties: {
43
+ 'templateOptions.maxDate': (model: any) => model?.before
44
+ }
45
+ },
46
+ {
47
+ type: 'date-time',
48
+ key: 'before',
49
+ templateOptions: {
50
+ label: gettext('to')
51
+ },
52
+ expressionProperties: {
53
+ 'templateOptions.minDate': (model: any) => model?.after
54
+ }
55
+ }
56
+ ],
57
+ validators: {
58
+ atLeastOneFilled: {
59
+ expression: (formGroup: any) => {
60
+ const after = formGroup.get('after').value;
61
+ const before = formGroup.get('before').value;
62
+ return after || before;
63
+ }
64
+ }
65
+ }
66
+ }
67
+ ],
68
+ formGroup: new FormGroup({}),
69
+ getFilter: model => {
70
+ const filter: any = {};
71
+ const dates = model?.lastUpdated;
72
+ if (dates?.after || dates?.before) {
73
+ filter.__and = [];
74
+ if (dates?.after) {
75
+ const after = this.formatDate(dates.after);
76
+ filter.__and.push({
77
+ 'lastUpdated.date': { __gt: after }
78
+ });
79
+ }
80
+ if (dates?.before) {
81
+ const before = this.formatDate(dates.before);
82
+ filter.__and.push({
83
+ 'lastUpdated.date': { __lt: before }
84
+ });
85
+ }
86
+ }
87
+ return filter;
88
+ }
89
+ };
90
+
91
+ this.sortable = true;
92
+ this.sortingConfig = {
93
+ pathSortingConfigs: [{ path: 'lastUpdated.date' }]
94
+ };
95
+ }
96
+
97
+ protected formatDate(dateToFormat: string): string {
98
+ return new Date(dateToFormat).toISOString();
99
+ }
100
+ }
@@ -1,6 +1,5 @@
1
- import { CommonModule } from '@angular/common';
2
1
  import { NgModule } from '@angular/core';
3
- import { hookNavigator, hookRoute, NavigatorNode } from '@c8y/ngx-components';
2
+ import { CommonModule, hookNavigator, hookRoute, NavigatorNode } from '@c8y/ngx-components';
4
3
  import { ServerGridActionControlsModule } from './server-grid-action-controls.module';
5
4
 
6
5
  @NgModule({
@@ -10,6 +10,8 @@ import {
10
10
  Pagination
11
11
  } from '@c8y/ngx-components';
12
12
 
13
+ import { assign, get, identity } from 'lodash-es';
14
+ import { LastUpdatedDataGridColumn } from './last-updated-data-grid-column/last-updated.data-grid-column';
13
15
  import { TypeDataGridColumn } from './type-data-grid-column/type.data-grid-column';
14
16
 
15
17
  /** Model for custom type filtering form. */
@@ -59,7 +61,8 @@ export class ServerGridExampleService {
59
61
  filterable: true,
60
62
  sortable: true
61
63
  },
62
- new TypeDataGridColumn()
64
+ new TypeDataGridColumn(),
65
+ new LastUpdatedDataGridColumn()
63
66
  ];
64
67
 
65
68
  return columns;
@@ -177,43 +180,6 @@ export class ServerGridExampleService {
177
180
  return { icon, label };
178
181
  }
179
182
 
180
- /** Returns a query object for given settings of filtering by type. */
181
- getTypeQuery(model: TypeFilteringModel): any {
182
- let query: any = {};
183
-
184
- if (model.group) {
185
- query = this.queriesUtil.addOrFilter(query, { type: 'c8y_DeviceGroup' });
186
- }
187
-
188
- if (model.device) {
189
- query = this.queriesUtil.addOrFilter(query, { __has: 'c8y_IsDevice' });
190
- }
191
-
192
- if (model.smartRule) {
193
- query = this.queriesUtil.addOrFilter(query, {
194
- type: { __in: ['c8y_SmartRule', 'c8y_PrivateSmartRule'] }
195
- });
196
- }
197
-
198
- if (model.dashboard) {
199
- query = this.queriesUtil.addOrFilter(query, {
200
- type: { __has: 'c8y_Dashboard' }
201
- });
202
- }
203
-
204
- if (model.file) {
205
- query = this.queriesUtil.addOrFilter(query, {
206
- type: { __has: 'c8y_IsBinary' }
207
- });
208
- }
209
-
210
- if (model.application) {
211
- query = this.queriesUtil.addOrFilter(query, { type: 'c8y_Application_*' });
212
- }
213
-
214
- return query;
215
- }
216
-
217
183
  /** Returns filters for given columns and pagination setup. */
218
184
  private getFilters(columns: Column[], pagination: Pagination) {
219
185
  return {
@@ -232,10 +198,11 @@ export class ServerGridExampleService {
232
198
  }
233
199
 
234
200
  /** Returns a query object based on columns setup. */
235
- private getQueryObj(columns: Column[]): any {
201
+ private getQueryObj(columns: Column[], defaultFilter = {}): any {
236
202
  return transform(columns, (query, column) => this.addColumnQuery(query, column), {
237
203
  __filter: {},
238
- __orderby: []
204
+ __orderby: [],
205
+ ...defaultFilter
239
206
  });
240
207
  }
241
208
 
@@ -251,7 +218,17 @@ export class ServerGridExampleService {
251
218
 
252
219
  // in the case of custom filtering form, we're storing the query in `externalFilterQuery.query`
253
220
  if (column.externalFilterQuery) {
254
- query = this.queriesUtil.addAndFilter(query, column.externalFilterQuery.query);
221
+ const getFilter = column.filteringConfig.getFilter || identity;
222
+ const queryObj = getFilter(column.externalFilterQuery);
223
+
224
+ if (queryObj.__or) {
225
+ query.__filter.__and = query.__filter.__and || [];
226
+ query.__filter.__and.push(queryObj);
227
+ } else if (queryObj.__and && get(query, '__filter.__and')) {
228
+ queryObj.__and.map(obj => query.__filter.__and.push(obj));
229
+ } else {
230
+ assign(query.__filter, queryObj);
231
+ }
255
232
  }
256
233
  }
257
234
 
@@ -1,47 +1,88 @@
1
1
  import { Type } from '@angular/core';
2
- import { Column, ColumnDataType, SortOrder, FilterPredicateFunction } from '@c8y/ngx-components';
3
- import { TypeHeaderCellRendererComponent } from './type.header-cell-renderer.component';
2
+ import { BaseColumn, ColumnConfig, PartialFilterChipGenerationType } from '@c8y/ngx-components';
4
3
  import { TypeCellRendererComponent } from './type.cell-renderer.component';
5
4
  import { TypeFilteringFormRendererComponent } from './type.filtering-form-renderer.component';
5
+ import { TypeHeaderCellRendererComponent } from './type.header-cell-renderer.component';
6
+
7
+ const FILTER_TYPES = [
8
+ { key: 'group', label: 'Group' },
9
+ { key: 'device', label: 'Device' },
10
+ { key: 'smartRule', label: 'Smart Rule' },
11
+ { key: 'dashboard', label: 'Dashboard' },
12
+ { key: 'file', label: 'File' },
13
+ { key: 'application', label: 'Application' }
14
+ ];
6
15
 
7
16
  /**
8
- * Defines a class for custom Type column.
9
- * Implements `Column` interface and sets basic properties, as well as custom components.
17
+ * Defines a custom Type column with custom filtering form and chips generation.
10
18
  */
11
- export class TypeDataGridColumn implements Column {
12
- name: string;
13
- path?: string;
14
- header?: string;
15
- dataType?: ColumnDataType;
16
-
17
- visible?: boolean;
18
- positionFixed?: boolean;
19
- gridTrackSize?: string;
19
+ export class TypeDataGridColumn extends BaseColumn {
20
+ readonly name = 'type';
21
+ readonly header = 'Type';
20
22
 
21
- headerCSSClassName?: string | string[];
22
- headerCellRendererComponent?: Type<any>;
23
+ headerCellRendererComponent: Type<any> = TypeHeaderCellRendererComponent;
24
+ cellRendererComponent: Type<any> = TypeCellRendererComponent;
25
+ sortable = false;
26
+ filterable = true;
27
+ filteringFormRendererComponent: Type<any> = TypeFilteringFormRendererComponent;
23
28
 
24
- cellCSSClassName?: string | string[];
25
- cellRendererComponent?: Type<any>;
29
+ constructor(initialColumnConfig?: ColumnConfig) {
30
+ super(initialColumnConfig);
26
31
 
27
- sortable?: boolean;
28
- sortOrder?: SortOrder;
32
+ // Set the custom filtering configuration
33
+ this.filteringConfig = {
34
+ /**
35
+ * Generates filter chips based on the selected filters in the model.
36
+ * Each chip represents a filter and provides a way for users to visualize
37
+ * and remove the applied filters.
38
+ *
39
+ * @param model An object with defined structure (e.g. by schema).
40
+ * @returns return an array of partial filter chips with required properties 'displayValue' and 'value'.
41
+ */
42
+ generateChips: (model): PartialFilterChipGenerationType[] => {
43
+ const chips = [];
29
44
 
30
- filterable?: boolean;
31
- filteringFormRendererComponent?: Type<any>;
32
- filterPredicate?: string | FilterPredicateFunction;
33
- externalFilterQuery?: string | object;
45
+ FILTER_TYPES.forEach(type => {
46
+ if (model[type.key]) {
47
+ chips.push({
48
+ displayValue: type.label,
49
+ value: type.key,
50
+ remove: () => {
51
+ delete model[type.key];
52
+ return {
53
+ externalFilterQuery: { ...model },
54
+ columnName: this.name
55
+ };
56
+ }
57
+ });
58
+ }
59
+ });
34
60
 
35
- constructor() {
36
- this.name = 'type';
37
- this.header = 'Type';
61
+ return chips;
62
+ },
63
+ /**
64
+ * Transforms a filtering config model (e.g. coming from schema form component) to a query object.
65
+ * However, using schema form component is not necessary.
66
+ * Model can be defined arbitrarily but must converted to a valid query object.
67
+ * @param model An object with defined structure (e.g. by schema).
68
+ * @returns A query object to be used to generate a query string (QueryUtils).
69
+ */
70
+ getFilter: model => {
71
+ const filter: any = {};
72
+ const ors = [];
38
73
 
39
- this.headerCellRendererComponent = TypeHeaderCellRendererComponent;
40
- this.cellRendererComponent = TypeCellRendererComponent;
74
+ if (model.group) ors.push({ type: 'c8y_DeviceGroup' });
75
+ if (model.device) ors.push({ __has: 'c8y_IsDevice' });
76
+ if (model.smartRule)
77
+ ors.push({ type: { __in: ['c8y_SmartRule', 'c8y_PrivateSmartRule'] } });
78
+ if (model.dashboard) ors.push({ type: { __has: 'c8y_Dashboard' } });
79
+ if (model.file) ors.push({ type: { __has: 'c8y_IsBinary' } });
80
+ if (model.application) ors.push({ type: 'c8y_Application_*' });
41
81
 
42
- this.filterable = true;
43
- this.filteringFormRendererComponent = TypeFilteringFormRendererComponent;
82
+ if (ors.length) filter.__or = ors;
44
83
 
45
- this.sortable = false;
84
+ return filter;
85
+ }
86
+ };
46
87
  }
47
88
  }
@@ -1,6 +1,6 @@
1
- import { Component, Inject } from '@angular/core';
1
+ import { Component } from '@angular/core';
2
2
  import { FilteringFormRendererContext, FormsModule } from '@c8y/ngx-components';
3
- import { ServerGridExampleService, TypeFilteringModel } from '../server-grid-example.service';
3
+ import { TypeFilteringModel } from '../server-grid-example.service';
4
4
 
5
5
  /**
6
6
  * This is the example component for custom filtering form.
@@ -79,12 +79,9 @@ import { ServerGridExampleService, TypeFilteringModel } from '../server-grid-exa
79
79
  export class TypeFilteringFormRendererComponent {
80
80
  model: TypeFilteringModel;
81
81
 
82
- constructor(
83
- public context: FilteringFormRendererContext,
84
- @Inject(ServerGridExampleService) public service: ServerGridExampleService
85
- ) {
82
+ constructor(public context: FilteringFormRendererContext) {
86
83
  // restores the settings from current column setup
87
- this.model = (this.context.property.externalFilterQuery || {}).model || {};
84
+ this.model = this.context.property.externalFilterQuery || {};
88
85
  }
89
86
 
90
87
  /**
@@ -94,10 +91,7 @@ export class TypeFilteringFormRendererComponent {
94
91
  */
95
92
  applyFilter() {
96
93
  this.context.applyFilter({
97
- externalFilterQuery: {
98
- model: this.model,
99
- query: this.service.getTypeQuery(this.model)
100
- }
94
+ externalFilterQuery: { ...this.model }
101
95
  });
102
96
  }
103
97