@c8y/ngx-components 1023.53.0 → 1023.55.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/alarms/index.d.ts +2 -1
  2. package/alarms/index.d.ts.map +1 -1
  3. package/application-access/list/index.d.ts +36 -0
  4. package/application-access/list/index.d.ts.map +1 -0
  5. package/application-access/user/application-access-user-details-wrapper/index.d.ts +33 -0
  6. package/application-access/user/application-access-user-details-wrapper/index.d.ts.map +1 -0
  7. package/application-access/user/index.d.ts +6 -0
  8. package/application-access/user/index.d.ts.map +1 -0
  9. package/asset-properties/index.d.ts +1 -1
  10. package/context-dashboard/index.d.ts +1 -1
  11. package/datapoints-export-selector/index.d.ts +2 -2
  12. package/device-enrolment/index.d.ts +6 -0
  13. package/device-enrolment/index.d.ts.map +1 -0
  14. package/device-enrolment/modal/index.d.ts +88 -0
  15. package/device-enrolment/modal/index.d.ts.map +1 -0
  16. package/device-grid/index.d.ts +1 -1
  17. package/device-list/index.d.ts +1 -1
  18. package/device-shell/index.d.ts +1 -1
  19. package/echart/index.d.ts +1 -1
  20. package/echart/models/index.d.ts +2 -2
  21. package/fesm2022/c8y-ngx-components-alarms-cockpit.mjs +1 -1
  22. package/fesm2022/c8y-ngx-components-alarms-devicemanagement.mjs +1 -1
  23. package/fesm2022/c8y-ngx-components-alarms.mjs.map +1 -1
  24. package/fesm2022/c8y-ngx-components-application-access-list.mjs +180 -0
  25. package/fesm2022/c8y-ngx-components-application-access-list.mjs.map +1 -0
  26. package/fesm2022/c8y-ngx-components-application-access-user-application-access-user-details-wrapper.mjs +113 -0
  27. package/fesm2022/c8y-ngx-components-application-access-user-application-access-user-details-wrapper.mjs.map +1 -0
  28. package/fesm2022/c8y-ngx-components-application-access-user.mjs +20 -0
  29. package/fesm2022/c8y-ngx-components-application-access-user.mjs.map +1 -0
  30. package/fesm2022/c8y-ngx-components-branding-shared.mjs +1 -1
  31. package/fesm2022/c8y-ngx-components-child-devices.mjs +1 -1
  32. package/fesm2022/c8y-ngx-components-context-dashboard-asset-add.mjs +1 -1
  33. package/fesm2022/c8y-ngx-components-context-dashboard-device-add.mjs +1 -1
  34. package/fesm2022/c8y-ngx-components-context-dashboard-devicemanagement.mjs +1 -1
  35. package/fesm2022/{c8y-ngx-components-dashboard-details-advanced-tab-dashboard-details-advanced-tab.component-D0C7SH6L.mjs → c8y-ngx-components-dashboard-details-advanced-tab-dashboard-details-advanced-tab.component-DvKsV_Fs.mjs} +14 -12
  36. package/fesm2022/c8y-ngx-components-dashboard-details-advanced-tab-dashboard-details-advanced-tab.component-DvKsV_Fs.mjs.map +1 -0
  37. package/fesm2022/c8y-ngx-components-dashboard-details-advanced-tab.mjs +2 -2
  38. package/fesm2022/c8y-ngx-components-datapoint-explorer-devicemanagement.mjs +1 -1
  39. package/fesm2022/c8y-ngx-components-datapoint-explorer.mjs +1 -1
  40. package/fesm2022/c8y-ngx-components-datapoint-library.mjs +1 -1
  41. package/fesm2022/c8y-ngx-components-device-enrolment-modal.mjs +570 -0
  42. package/fesm2022/c8y-ngx-components-device-enrolment-modal.mjs.map +1 -0
  43. package/fesm2022/c8y-ngx-components-device-enrolment.mjs +147 -0
  44. package/fesm2022/c8y-ngx-components-device-enrolment.mjs.map +1 -0
  45. package/fesm2022/c8y-ngx-components-ecosystem.mjs +2 -2
  46. package/fesm2022/c8y-ngx-components-ecosystem.mjs.map +1 -1
  47. package/fesm2022/c8y-ngx-components-exports.mjs +1 -1
  48. package/fesm2022/c8y-ngx-components-feature-toggles.mjs +1 -1
  49. package/fesm2022/c8y-ngx-components-register-device.mjs +1 -1
  50. package/fesm2022/c8y-ngx-components-remote-access-ssh.mjs +1 -1
  51. package/fesm2022/c8y-ngx-components-remote-access-telnet.mjs +1 -1
  52. package/fesm2022/c8y-ngx-components-remote-access-vnc.mjs +1 -1
  53. package/fesm2022/c8y-ngx-components-report-dashboard.mjs +1 -1
  54. package/fesm2022/c8y-ngx-components-repository-firmware.mjs +1 -1
  55. package/fesm2022/c8y-ngx-components-repository-shared.mjs +3 -5
  56. package/fesm2022/c8y-ngx-components-repository-shared.mjs.map +1 -1
  57. package/fesm2022/c8y-ngx-components-sub-assets.mjs +132 -55
  58. package/fesm2022/c8y-ngx-components-sub-assets.mjs.map +1 -1
  59. package/fesm2022/c8y-ngx-components-tenants.mjs +71 -32
  60. package/fesm2022/c8y-ngx-components-tenants.mjs.map +1 -1
  61. package/fesm2022/c8y-ngx-components-upgrade.mjs +7 -1
  62. package/fesm2022/c8y-ngx-components-upgrade.mjs.map +1 -1
  63. package/fesm2022/c8y-ngx-components-widgets-implementations-cockpit-welcome.mjs +4 -4
  64. package/fesm2022/c8y-ngx-components-widgets-implementations-cockpit-welcome.mjs.map +1 -1
  65. package/fesm2022/c8y-ngx-components.mjs +454 -225
  66. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  67. package/index.d.ts +109 -39
  68. package/index.d.ts.map +1 -1
  69. package/locales/de.po +184 -13
  70. package/locales/es.po +183 -13
  71. package/locales/fr.po +183 -13
  72. package/locales/ja_JP.po +162 -13
  73. package/locales/ko.po +184 -13
  74. package/locales/locales.pot +157 -8
  75. package/locales/nl.po +183 -13
  76. package/locales/pl.po +183 -13
  77. package/locales/pt_BR.po +183 -13
  78. package/locales/zh_CN.po +184 -13
  79. package/locales/zh_TW.po +183 -13
  80. package/map/index.d.ts +1 -0
  81. package/map/index.d.ts.map +1 -1
  82. package/operations/bulk-operations-service/index.d.ts +1 -1
  83. package/package.json +1 -1
  84. package/protocol-opcua/mappings/index.d.ts +1 -1
  85. package/register-device/index.d.ts +1 -1
  86. package/remote-access/data/index.d.ts +1 -0
  87. package/remote-access/data/index.d.ts.map +1 -1
  88. package/repository/shared/index.d.ts.map +1 -1
  89. package/sub-assets/index.d.ts +9 -3
  90. package/sub-assets/index.d.ts.map +1 -1
  91. package/tenants/index.d.ts +1 -0
  92. package/tenants/index.d.ts.map +1 -1
  93. package/upgrade/index.d.ts.map +1 -1
  94. package/widgets/implementations/asset-table/index.d.ts +1 -1
  95. package/widgets/implementations/datapoints-table/index.d.ts +2 -2
  96. package/widgets/implementations/kpi/index.d.ts +1 -1
  97. package/fesm2022/c8y-ngx-components-dashboard-details-advanced-tab-dashboard-details-advanced-tab.component-D0C7SH6L.mjs.map +0 -1
@@ -11,7 +11,7 @@ import * as i4 from '@c8y/ngx-components/assets-navigator';
11
11
  import { AssetTypeGridColumn, AssetTypeCellRendererComponent } from '@c8y/ngx-components/data-grid-columns/asset-type';
12
12
  import * as i1 from '@ngx-translate/core';
13
13
  import { firstValueFrom, catchError, of, Subject, delay, takeUntil as takeUntil$1, tap } from 'rxjs';
14
- import { NgIf, NgClass, NgTemplateOutlet, NgStyle, NgSwitch, NgSwitchCase, NgFor, NgSwitchDefault, AsyncPipe } from '@angular/common';
14
+ import { NgIf, NgClass, NgTemplateOutlet, NgStyle, AsyncPipe } from '@angular/common';
15
15
  import { CdkTrapFocus } from '@angular/cdk/a11y';
16
16
  import { CdkStep } from '@angular/cdk/stepper';
17
17
  import * as i4$1 from '@c8y/ngx-components/device-list';
@@ -76,15 +76,27 @@ class SubAssetsService extends DataGridService {
76
76
  this.IS_DYNAMIC_GROUP_FRAGMENT = 'c8y_IsDynamicGroup';
77
77
  }
78
78
  async getCustomProperties(group) {
79
- const assetType$ = this.assetTypes.getAssetTypeByName$(group.type);
80
- const assetType = await firstValueFrom(assetType$);
81
- if (assetType) {
79
+ const assetType = await firstValueFrom(this.assetTypes.getAssetTypeByName$(group.type));
80
+ if (assetType?.c8y_IsAssetType) {
82
81
  const { data } = await this.inventoryService.childAdditionsList(assetType, {
83
82
  pageSize: 2000,
84
83
  query: "$filter=(has('c8y_IsAssetProperty'))"
85
84
  });
86
85
  return data;
87
86
  }
87
+ else if (assetType?.c8y_JsonSchema?.definitions) {
88
+ const data = [];
89
+ Object.entries(assetType.c8y_JsonSchema.definitions).forEach(([key, value]) => {
90
+ const required = assetType.c8y_JsonSchema.required?.includes(key) || false;
91
+ if (typeof value === 'object' && value !== null) {
92
+ data.push({ ...value, key, isRequired: required });
93
+ }
94
+ else {
95
+ data.push({ key, isRequired: required });
96
+ }
97
+ });
98
+ return data;
99
+ }
88
100
  return [];
89
101
  }
90
102
  getDefaultColumns(_filterable = true, _sortable = true) {
@@ -475,7 +487,7 @@ class DeleteAssetsModalComponent {
475
487
  this.deleteGroupSubAssetsMsg = this.translateService.instant(gettext('Also delete all devices inside "{{name}}" and its subassets.'), { name: this.asset.name });
476
488
  }
477
489
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DeleteAssetsModalComponent, deps: [{ token: i1.TranslateService }, { token: i3.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
478
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: DeleteAssetsModalComponent, isStandalone: true, selector: "c8y-delete-assets-modal", inputs: { showWithCascadeCheckbox: "showWithCascadeCheckbox", showWithDeviceUserCheckbox: "showWithDeviceUserCheckbox", asset: "asset" }, viewQueries: [{ propertyName: "modalRef", first: true, predicate: ["modalRef"], descendants: true }], ngImport: i0, template: "<c8y-confirm-modal [title]=\"title\" [status]=\"status\" [labels]=\"labels\" #modalRef>\n <form #assetsForm=\"ngForm\">\n <p class=\"text-wrap m-b-16\">\n {{ message | translate }}\n </p>\n <c8y-form-group *ngIf=\"showWithCascadeCheckbox\" class=\"m-b-0\">\n <label title=\"{{ 'Delete devices' | translate }}\" class=\"c8y-checkbox\">\n <input\n type=\"checkbox\"\n name=\"cascade\"\n [(ngModel)]=\"config.cascade\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.DELETE_ASSET.COMPONENTS.DELETE_ASSETS_MODAL,\n action: PRODUCT_EXPERIENCE.DELETE_ASSET.ACTIONS.CASCADE_DELETE\n }\"\n [disabled]=\"config?.withDeviceUser\"\n />\n <span></span>\n <span class=\"text-break-word\">\n {{ deleteGroupSubAssetsMsg | translate }}\n </span>\n </label>\n </c8y-form-group>\n <c8y-form-group *ngIf=\"showWithDeviceUserCheckbox\" class=\"m-b-0\">\n <label title=\"{{ 'Delete associated device owner' | translate }}\" class=\"c8y-checkbox\">\n <input\n type=\"checkbox\"\n name=\"withDeviceUser\"\n [(ngModel)]=\"config.withDeviceUser\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.DELETE_ASSET.COMPONENTS.DELETE_ASSETS_MODAL,\n action: PRODUCT_EXPERIENCE.DELETE_ASSET.ACTIONS.DELETE_DEVICE_OWNER\n }\"\n [disabled]=\"config?.cascade\"\n />\n <span></span>\n <span>\n {{ 'Also delete associated device owner.' | translate }}\n </span>\n </label>\n </c8y-form-group>\n </form>\n</c8y-confirm-modal>\n", dependencies: [{ kind: "component", type: ConfirmModalComponent, selector: "c8y-confirm-modal", inputs: ["title", "body", "confirmOptions", "status", "labels"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
490
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: DeleteAssetsModalComponent, isStandalone: true, selector: "c8y-delete-assets-modal", inputs: { showWithCascadeCheckbox: "showWithCascadeCheckbox", showWithDeviceUserCheckbox: "showWithDeviceUserCheckbox", asset: "asset" }, viewQueries: [{ propertyName: "modalRef", first: true, predicate: ["modalRef"], descendants: true }], ngImport: i0, template: "<c8y-confirm-modal [title]=\"title\" [status]=\"status\" [labels]=\"labels\" #modalRef>\n <form #assetsForm=\"ngForm\">\n <p class=\"text-wrap m-b-16\">\n {{ message | translate }}\n </p>\n <c8y-form-group *ngIf=\"showWithCascadeCheckbox\" class=\"m-b-0\">\n <label title=\"{{ 'Delete devices' | translate }}\" class=\"c8y-checkbox\">\n <input\n type=\"checkbox\"\n name=\"cascade\"\n [(ngModel)]=\"config.cascade\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.DELETE_ASSET.COMPONENTS.DELETE_ASSETS_MODAL,\n action: PRODUCT_EXPERIENCE.DELETE_ASSET.ACTIONS.CASCADE_DELETE\n }\"\n [disabled]=\"config?.withDeviceUser\"\n />\n <span></span>\n <span class=\"text-break-word\">\n {{ deleteGroupSubAssetsMsg | translate }}\n </span>\n </label>\n </c8y-form-group>\n <c8y-form-group *ngIf=\"showWithDeviceUserCheckbox\" class=\"m-b-0\">\n <label title=\"{{ 'Delete associated device owner' | translate }}\" class=\"c8y-checkbox\">\n <input\n type=\"checkbox\"\n name=\"withDeviceUser\"\n [(ngModel)]=\"config.withDeviceUser\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.DELETE_ASSET.COMPONENTS.DELETE_ASSETS_MODAL,\n action: PRODUCT_EXPERIENCE.DELETE_ASSET.ACTIONS.DELETE_DEVICE_OWNER\n }\"\n [disabled]=\"config?.cascade\"\n />\n <span></span>\n <span>\n {{ 'Also delete associated device owner.' | translate }}\n </span>\n </label>\n </c8y-form-group>\n </form>\n</c8y-confirm-modal>\n", dependencies: [{ kind: "component", type: ConfirmModalComponent, selector: "c8y-confirm-modal", inputs: ["title", "body", "confirmOptions", "status", "requireCodeVerification", "labels"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
479
491
  }
480
492
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DeleteAssetsModalComponent, decorators: [{
481
493
  type: Component,
@@ -617,7 +629,7 @@ class UnassignModalComponent {
617
629
  this.closeSubject.complete();
618
630
  }
619
631
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: UnassignModalComponent, deps: [{ token: i1.TranslateService }, { token: i3.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
620
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: UnassignModalComponent, isStandalone: true, selector: "c8y-unassign-modal", inputs: { asset: "asset" }, viewQueries: [{ propertyName: "modalRef", first: true, predicate: ["modalRef"], descendants: true }], ngImport: i0, template: "<c8y-confirm-modal [title]=\"title\" [status]=\"status\" [labels]=\"labels\" #modalRef>\n <span>{{ message | translate }}</span>\n</c8y-confirm-modal>\n", dependencies: [{ kind: "component", type: ConfirmModalComponent, selector: "c8y-confirm-modal", inputs: ["title", "body", "confirmOptions", "status", "labels"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
632
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: UnassignModalComponent, isStandalone: true, selector: "c8y-unassign-modal", inputs: { asset: "asset" }, viewQueries: [{ propertyName: "modalRef", first: true, predicate: ["modalRef"], descendants: true }], ngImport: i0, template: "<c8y-confirm-modal [title]=\"title\" [status]=\"status\" [labels]=\"labels\" #modalRef>\n <span>{{ message | translate }}</span>\n</c8y-confirm-modal>\n", dependencies: [{ kind: "component", type: ConfirmModalComponent, selector: "c8y-confirm-modal", inputs: ["title", "body", "confirmOptions", "status", "requireCodeVerification", "labels"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
621
633
  }
622
634
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: UnassignModalComponent, decorators: [{
623
635
  type: Component,
@@ -1410,7 +1422,7 @@ class AssetPropertiesItemComponent {
1410
1422
  formComplexPropsValue() {
1411
1423
  const complexProps = {};
1412
1424
  this.complex.forEach(complexObj => {
1413
- if (complexObj.file) {
1425
+ if (complexObj.file || complexObj.type === 'date') {
1414
1426
  complexProps[complexObj.key] = complexObj.value;
1415
1427
  }
1416
1428
  else {
@@ -1445,21 +1457,11 @@ class AssetPropertiesItemComponent {
1445
1457
  return this.filesService.toBase64(imageFile);
1446
1458
  }
1447
1459
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertiesItemComponent, deps: [{ token: i3.AlertService }, { token: i3.C8yJSONSchema }, { token: i3.FilesService }], target: i0.ɵɵFactoryTarget.Component }); }
1448
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: AssetPropertiesItemComponent, isStandalone: true, selector: "c8y-asset-properties-item", inputs: { key: "key", value: "value", label: "label", type: "type", file: "file", complex: "complex", isEdit: "isEdit", jsonSchema: "jsonSchema" }, usesOnChanges: true, ngImport: i0, template: "<ng-container [ngSwitch]=\"type\" *ngIf=\"!isEdit\">\n <ng-container *ngSwitchCase=\"'date'\">\n {{ (value | c8yDate: 'fullDate') || ('Undefined' | translate) }}\n </ng-container>\n <ng-container *ngSwitchCase=\"'file'\">\n <ng-container *ngIf=\"file\">\n <img *ngIf=\"previewImage\" [src]=\"previewImage\" class=\"img-responsive\" />\n <button\n *ngIf=\"!previewImage\"\n (click)=\"filesService.download(file)\"\n type=\"button\"\n title=\"{{ 'Download' | translate }} {{ file.name }}\"\n class=\"btn btn-clean text-truncate p-0\"\n >\n {{ file.name }}\n </button>\n </ng-container>\n <ng-container *ngIf=\"!file\">\n {{ 'No file attached.' | translate }}\n </ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'object'\">\n <ul class=\"list-unstyled c8y-custom-properties\">\n <li\n *ngFor=\"let prop of complex; let i = index\"\n [ngClass]=\"{ 'separator-top-bottom': i === 0, 'separator-bottom': i > 0 }\"\n class=\"p-t-4 p-b-4 d-flex text-nowrap\"\n >\n <label\n class=\"small m-b-0 m-r-8 text-truncate\"\n title=\"{{ prop.label | translate }}\"\n [ngClass]=\"{ 'a-s-start': prop.file }\"\n >\n {{ prop.label | translate }}\n </label>\n <span class=\"m-l-auto\" style=\"max-width: {{ prop.file ? '50%' : '100%' }}; min-width:0;\">\n <c8y-asset-properties-item\n [file]=\"prop.file\"\n [key]=\"prop.key\"\n [type]=\"prop.type\"\n [value]=\"prop.value\"\n ></c8y-asset-properties-item>\n </span>\n </li>\n </ul>\n </ng-container>\n <!--\n <ng-container *ngSwitchCase=\"'boolean'\">\n <input type=\"checkbox\" [checked]=\"value\" [disabled]=\"true\" />\n </ng-container>\n -->\n <ng-container *ngSwitchCase=\"type === 'number' || type === 'boolean' ? type : ''\">\n <p class=\"text-truncate\" title=\"{{ value != null ? value : ('Undefined' | translate) }}\">\n {{ value != null ? value : ('Undefined' | translate) }}\n </p>\n </ng-container>\n <ng-container *ngSwitchDefault>\n <p class=\"text-truncate\" title=\"{{ (value | translate) || ('Undefined' | translate) }}\">\n {{ (value | translate) || ('Undefined' | translate) }}\n </p>\n </ng-container>\n</ng-container>\n<formly-form *ngIf=\"isEdit\" [form]=\"form\" [fields]=\"fields\" [model]=\"model\"></formly-form>\n", dependencies: [{ kind: "component", type: AssetPropertiesItemComponent, selector: "c8y-asset-properties-item", inputs: ["key", "value", "label", "type", "file", "complex", "isEdit", "jsonSchema"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormlyModule }, { kind: "component", type: i2$2.FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }] }); }
1460
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AssetPropertiesItemComponent, isStandalone: true, selector: "c8y-asset-properties-item", inputs: { key: "key", value: "value", label: "label", type: "type", file: "file", complex: "complex", isEdit: "isEdit", jsonSchema: "jsonSchema" }, usesOnChanges: true, ngImport: i0, template: "@if (!isEdit) {\n @switch (type) {\n @case ('date') {\n {{ (value | c8yDate: 'fullDate') || ('Undefined' | translate) }}\n }\n @case ('file') {\n @if (file) {\n @if (previewImage) {\n <img\n class=\"img-responsive\"\n [src]=\"previewImage\"\n />\n }\n @if (!previewImage) {\n <button\n class=\"btn btn-clean text-truncate p-0\"\n title=\"{{ 'Download' | translate }} {{ file.name }}\"\n type=\"button\"\n (click)=\"filesService.download(file)\"\n >\n {{ file.name }}\n </button>\n }\n } @else {\n {{ 'No file attached.' | translate }}\n }\n }\n @case ('object') {\n <ul class=\"list-unstyled c8y-custom-properties\">\n @for (prop of complex; track prop.key; let i = $index) {\n <li\n class=\"p-t-4 p-b-4 d-flex text-nowrap\"\n [ngClass]=\"{ 'separator-top-bottom': i === 0, 'separator-bottom': i > 0 }\"\n >\n <label\n class=\"small m-b-0 m-r-8 text-truncate\"\n title=\"{{ prop.label | translate }}\"\n [ngClass]=\"{ 'a-s-start': prop.file }\"\n >\n {{ prop.label | translate }}\n </label>\n <span\n class=\"m-l-auto\"\n [ngStyle]=\"{\n 'max-width': prop.file ? '50%' : '100%',\n 'min-width': '0'\n }\"\n >\n <c8y-asset-properties-item\n [file]=\"prop.file\"\n [key]=\"prop.key\"\n [type]=\"prop.type\"\n [value]=\"prop.value\"\n ></c8y-asset-properties-item>\n </span>\n </li>\n }\n </ul>\n }\n @case (type === 'number' || type === 'boolean' ? type : '') {\n <p\n class=\"text-truncate\"\n title=\"{{ value != null ? value : ('Undefined' | translate) }}\"\n >\n {{ value != null ? value : ('Undefined' | translate) }}\n </p>\n }\n @default {\n <p\n class=\"text-truncate\"\n title=\"{{ (value | translate) || ('Undefined' | translate) }}\"\n >\n {{ (value | translate) || ('Undefined' | translate) }}\n </p>\n }\n }\n}\n@if (isEdit) {\n <formly-form\n [form]=\"form\"\n [fields]=\"fields\"\n [model]=\"model\"\n ></formly-form>\n}\n", dependencies: [{ kind: "component", type: AssetPropertiesItemComponent, selector: "c8y-asset-properties-item", inputs: ["key", "value", "label", "type", "file", "complex", "isEdit", "jsonSchema"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "component", type: i2$2.FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }] }); }
1449
1461
  }
1450
1462
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertiesItemComponent, decorators: [{
1451
1463
  type: Component,
1452
- args: [{ selector: 'c8y-asset-properties-item', imports: [
1453
- NgIf,
1454
- NgSwitch,
1455
- NgSwitchCase,
1456
- NgFor,
1457
- NgClass,
1458
- NgSwitchDefault,
1459
- FormlyModule,
1460
- C8yTranslatePipe,
1461
- DatePipe
1462
- ], template: "<ng-container [ngSwitch]=\"type\" *ngIf=\"!isEdit\">\n <ng-container *ngSwitchCase=\"'date'\">\n {{ (value | c8yDate: 'fullDate') || ('Undefined' | translate) }}\n </ng-container>\n <ng-container *ngSwitchCase=\"'file'\">\n <ng-container *ngIf=\"file\">\n <img *ngIf=\"previewImage\" [src]=\"previewImage\" class=\"img-responsive\" />\n <button\n *ngIf=\"!previewImage\"\n (click)=\"filesService.download(file)\"\n type=\"button\"\n title=\"{{ 'Download' | translate }} {{ file.name }}\"\n class=\"btn btn-clean text-truncate p-0\"\n >\n {{ file.name }}\n </button>\n </ng-container>\n <ng-container *ngIf=\"!file\">\n {{ 'No file attached.' | translate }}\n </ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'object'\">\n <ul class=\"list-unstyled c8y-custom-properties\">\n <li\n *ngFor=\"let prop of complex; let i = index\"\n [ngClass]=\"{ 'separator-top-bottom': i === 0, 'separator-bottom': i > 0 }\"\n class=\"p-t-4 p-b-4 d-flex text-nowrap\"\n >\n <label\n class=\"small m-b-0 m-r-8 text-truncate\"\n title=\"{{ prop.label | translate }}\"\n [ngClass]=\"{ 'a-s-start': prop.file }\"\n >\n {{ prop.label | translate }}\n </label>\n <span class=\"m-l-auto\" style=\"max-width: {{ prop.file ? '50%' : '100%' }}; min-width:0;\">\n <c8y-asset-properties-item\n [file]=\"prop.file\"\n [key]=\"prop.key\"\n [type]=\"prop.type\"\n [value]=\"prop.value\"\n ></c8y-asset-properties-item>\n </span>\n </li>\n </ul>\n </ng-container>\n <!--\n <ng-container *ngSwitchCase=\"'boolean'\">\n <input type=\"checkbox\" [checked]=\"value\" [disabled]=\"true\" />\n </ng-container>\n -->\n <ng-container *ngSwitchCase=\"type === 'number' || type === 'boolean' ? type : ''\">\n <p class=\"text-truncate\" title=\"{{ value != null ? value : ('Undefined' | translate) }}\">\n {{ value != null ? value : ('Undefined' | translate) }}\n </p>\n </ng-container>\n <ng-container *ngSwitchDefault>\n <p class=\"text-truncate\" title=\"{{ (value | translate) || ('Undefined' | translate) }}\">\n {{ (value | translate) || ('Undefined' | translate) }}\n </p>\n </ng-container>\n</ng-container>\n<formly-form *ngIf=\"isEdit\" [form]=\"form\" [fields]=\"fields\" [model]=\"model\"></formly-form>\n" }]
1464
+ args: [{ selector: 'c8y-asset-properties-item', imports: [NgClass, FormlyModule, C8yTranslatePipe, DatePipe], template: "@if (!isEdit) {\n @switch (type) {\n @case ('date') {\n {{ (value | c8yDate: 'fullDate') || ('Undefined' | translate) }}\n }\n @case ('file') {\n @if (file) {\n @if (previewImage) {\n <img\n class=\"img-responsive\"\n [src]=\"previewImage\"\n />\n }\n @if (!previewImage) {\n <button\n class=\"btn btn-clean text-truncate p-0\"\n title=\"{{ 'Download' | translate }} {{ file.name }}\"\n type=\"button\"\n (click)=\"filesService.download(file)\"\n >\n {{ file.name }}\n </button>\n }\n } @else {\n {{ 'No file attached.' | translate }}\n }\n }\n @case ('object') {\n <ul class=\"list-unstyled c8y-custom-properties\">\n @for (prop of complex; track prop.key; let i = $index) {\n <li\n class=\"p-t-4 p-b-4 d-flex text-nowrap\"\n [ngClass]=\"{ 'separator-top-bottom': i === 0, 'separator-bottom': i > 0 }\"\n >\n <label\n class=\"small m-b-0 m-r-8 text-truncate\"\n title=\"{{ prop.label | translate }}\"\n [ngClass]=\"{ 'a-s-start': prop.file }\"\n >\n {{ prop.label | translate }}\n </label>\n <span\n class=\"m-l-auto\"\n [ngStyle]=\"{\n 'max-width': prop.file ? '50%' : '100%',\n 'min-width': '0'\n }\"\n >\n <c8y-asset-properties-item\n [file]=\"prop.file\"\n [key]=\"prop.key\"\n [type]=\"prop.type\"\n [value]=\"prop.value\"\n ></c8y-asset-properties-item>\n </span>\n </li>\n }\n </ul>\n }\n @case (type === 'number' || type === 'boolean' ? type : '') {\n <p\n class=\"text-truncate\"\n title=\"{{ value != null ? value : ('Undefined' | translate) }}\"\n >\n {{ value != null ? value : ('Undefined' | translate) }}\n </p>\n }\n @default {\n <p\n class=\"text-truncate\"\n title=\"{{ (value | translate) || ('Undefined' | translate) }}\"\n >\n {{ (value | translate) || ('Undefined' | translate) }}\n </p>\n }\n }\n}\n@if (isEdit) {\n <formly-form\n [form]=\"form\"\n [fields]=\"fields\"\n [model]=\"model\"\n ></formly-form>\n}\n" }]
1463
1465
  }], ctorParameters: () => [{ type: i3.AlertService }, { type: i3.C8yJSONSchema }, { type: i3.FilesService }], propDecorators: { key: [{
1464
1466
  type: Input
1465
1467
  }], value: [{
@@ -1716,10 +1718,14 @@ class AssetPropertiesComponent {
1716
1718
  }
1717
1719
  async loadAsset() {
1718
1720
  this.isLoading = true;
1719
- const assetType$ = this.assetTypes.getAssetTypeByName$(this.asset.type);
1720
- this.assetType = await firstValueFrom(assetType$);
1721
+ this.assetType = await firstValueFrom(this.assetTypes.getAssetTypeByName$(this.asset.type));
1721
1722
  try {
1722
- this.properties = this.keepOrder(this.assetType?.c8y_IsAssetType?.properties, this.properties);
1723
+ const sourceArray = this.assetType?.c8y_IsAssetType?.properties ||
1724
+ this.assetType?.c8y_AllowedPropertyDefinitions;
1725
+ const keyField = this.assetType?.c8y_IsAssetType?.properties ? 'id' : 'identifier';
1726
+ if (sourceArray) {
1727
+ this.properties = this.keepOrder(sourceArray, this.properties, keyField);
1728
+ }
1723
1729
  }
1724
1730
  catch (ex) {
1725
1731
  console.warn(ex);
@@ -1729,16 +1735,41 @@ class AssetPropertiesComponent {
1729
1735
  }
1730
1736
  async resolveCustomProperties(managedObjects) {
1731
1737
  const properties = [];
1732
- for (const mo of managedObjects) {
1733
- if (mo.c8y_JsonSchema) {
1734
- const [item] = await this.parseItem(mo, mo.c8y_JsonSchema.properties, this.asset);
1735
- this.setItemRequired(item, mo);
1738
+ for (const property of managedObjects) {
1739
+ let item;
1740
+ if (property.c8y_JsonSchema) {
1741
+ [item] = await this.parseItem(property, property.c8y_JsonSchema.properties, this.asset);
1742
+ }
1743
+ else if (property.key) {
1744
+ this.transformType(property);
1745
+ const jsonSchema = {};
1746
+ jsonSchema[property.key] = property;
1747
+ [item] = await this.parseItem(null, jsonSchema, this.asset);
1748
+ }
1749
+ if (item) {
1750
+ this.setItemRequired(item, property);
1736
1751
  this.updatePositionKeyLabel(item);
1737
1752
  properties.push(item);
1738
1753
  }
1739
1754
  }
1740
1755
  return properties;
1741
1756
  }
1757
+ transformType(property) {
1758
+ if (property.format === 'date-time') {
1759
+ property.type = 'date';
1760
+ }
1761
+ else if (this.isFileTypeProperty(property)) {
1762
+ property.type = 'file';
1763
+ property.contentMediaType = property.properties?.binaryId?.contentMediaType;
1764
+ property.maxSize = property.properties?.binarySize?.maximum;
1765
+ }
1766
+ else if (property.enum) {
1767
+ property.type = 'enum';
1768
+ }
1769
+ }
1770
+ isFileTypeProperty(property) {
1771
+ return property.required?.[0] === 'binaryId';
1772
+ }
1742
1773
  deleteTitleFromMOJsonSchema(mo) {
1743
1774
  const schemaProperties = mo?.c8y_JsonSchema?.properties;
1744
1775
  const property = Object.keys(schemaProperties || {})[0];
@@ -1746,13 +1777,35 @@ class AssetPropertiesComponent {
1746
1777
  }
1747
1778
  /**
1748
1779
  * This method is used to order the complex properties in the order specified by the user in asset properties screen.
1749
- * @param mo - Managed object of the complex property associated with the asset.
1780
+ * @param mo - Managed object of the complex property associated with the asset (old structure) or properties object (new structure).
1750
1781
  */
1751
1782
  orderComplexProperties(mo) {
1752
- const complexProperties = mo.c8y_JsonSchema.properties[mo.name]?.['properties'];
1753
- const keyValuesArray = toPairs(complexProperties);
1783
+ const target = mo.c8y_JsonSchema ? mo.c8y_JsonSchema.properties[mo.name]?.['properties'] : mo;
1784
+ const keyValuesArray = toPairs(target);
1754
1785
  const orderedProperties = sortBy(keyValuesArray, ([, value]) => value.order);
1755
- mo.c8y_JsonSchema.properties[mo.name]['properties'] = fromPairs(orderedProperties);
1786
+ const result = fromPairs(orderedProperties);
1787
+ if (mo.c8y_JsonSchema) {
1788
+ mo.c8y_JsonSchema.properties[mo.name]['properties'] = result;
1789
+ return mo;
1790
+ }
1791
+ return result;
1792
+ }
1793
+ async addOrderDetails(properties, key) {
1794
+ const propDetails = await this.getPropertyDetails(key);
1795
+ propDetails.c8y_Order?.forEach(item => {
1796
+ if (properties[item.identifier]) {
1797
+ properties[item.identifier]['order'] = item.order;
1798
+ }
1799
+ });
1800
+ }
1801
+ async getPropertyDetails(identifier) {
1802
+ const query = {
1803
+ __filter: {
1804
+ __and: [{ type: 'c8y_PropertyDefinition' }, { c8y_Contexts: 'asset' }, { name: identifier }]
1805
+ }
1806
+ };
1807
+ const { data } = await this.inventory.listQuery(query);
1808
+ return data[0];
1756
1809
  }
1757
1810
  async parseItem(mo, properties, asset) {
1758
1811
  if (!asset) {
@@ -1776,9 +1829,17 @@ class AssetPropertiesComponent {
1776
1829
  value = !isNaN(valueDate.getTime()) ? valueDate : '';
1777
1830
  }
1778
1831
  if (type === 'object') {
1779
- // remove title to avoid excessive property name on asset complex properties form
1780
- this.deleteTitleFromMOJsonSchema(mo);
1781
- this.orderComplexProperties(mo);
1832
+ if (mo?.c8y_JsonSchema) {
1833
+ this.deleteTitleFromMOJsonSchema(mo);
1834
+ this.orderComplexProperties(mo);
1835
+ }
1836
+ else {
1837
+ for (const [, value] of Object.entries(properties[key].properties)) {
1838
+ this.transformType(value);
1839
+ }
1840
+ await this.addOrderDetails(properties[key].properties, key);
1841
+ properties[key].properties = this.orderComplexProperties(properties[key].properties);
1842
+ }
1782
1843
  if (!value) {
1783
1844
  value = {};
1784
1845
  for (const prop in properties[key].properties) {
@@ -1786,18 +1847,19 @@ class AssetPropertiesComponent {
1786
1847
  }
1787
1848
  }
1788
1849
  }
1850
+ const schema = mo?.c8y_JsonSchema || { properties: properties };
1789
1851
  items.push({
1790
1852
  key,
1791
1853
  value,
1792
- label: title || mo.label,
1854
+ label: title || mo?.label,
1793
1855
  type,
1794
- description: mo.description,
1856
+ description: mo?.description || properties[key].description,
1795
1857
  file,
1796
1858
  complex: type === 'object'
1797
1859
  ? await this.parseItem(mo, properties[key].properties, value)
1798
1860
  : undefined,
1799
1861
  isEdit: false,
1800
- jsonSchema: mo.c8y_JsonSchema
1862
+ jsonSchema: schema
1801
1863
  });
1802
1864
  }
1803
1865
  return items;
@@ -1818,11 +1880,16 @@ class AssetPropertiesComponent {
1818
1880
  try {
1819
1881
  if (prop.type === 'object') {
1820
1882
  this.updateUndefinedToPropTypeValue(prop, propertyValue[prop.key]);
1883
+ for (const currentProp of prop.complex) {
1884
+ if (currentProp.type === 'file') {
1885
+ await this.uploadFileProperty(propertyValue, prop, currentProp);
1886
+ }
1887
+ }
1821
1888
  }
1822
1889
  else {
1823
1890
  this.updateUndefinedToPropTypeValue(prop, propertyValue);
1891
+ propertyValue = await this.uploadFiles(propertyValue, prop.value);
1824
1892
  }
1825
- propertyValue = await this.uploadFiles(propertyValue, prop.value);
1826
1893
  // Avoid making a PUT request containing just the id, as response body might be incomplete
1827
1894
  const hasValues = Object.values(propertyValue).some(value => value !== undefined);
1828
1895
  if (!hasValues) {
@@ -1842,6 +1909,12 @@ class AssetPropertiesComponent {
1842
1909
  this.toggleEdit(prop);
1843
1910
  }
1844
1911
  }
1912
+ async uploadFileProperty(propertyValue, prop, currentProp) {
1913
+ const fileInfo = {};
1914
+ fileInfo[currentProp.key] = propertyValue[prop.key][currentProp.key];
1915
+ const value = await this.uploadFiles(fileInfo, currentProp.value);
1916
+ propertyValue[prop.key][currentProp.key] = value[currentProp.key];
1917
+ }
1845
1918
  updateUndefinedToPropTypeValue(prop, propertyValue) {
1846
1919
  for (const [key, value] of Object.entries(propertyValue)) {
1847
1920
  const property = prop.complex ? find(prop.complex, { key: key }) : prop;
@@ -1859,15 +1932,15 @@ class AssetPropertiesComponent {
1859
1932
  return '';
1860
1933
  }
1861
1934
  }
1862
- keepOrder(correctOrderedIds, properties) {
1863
- const orderedProperties = correctOrderedIds.map(({ id }) => {
1864
- const foundProperty = properties.find(property => property.id === id);
1935
+ keepOrder(correctOrderedIds, properties, keyField) {
1936
+ return correctOrderedIds.map(item => {
1937
+ const itemKey = item[keyField];
1938
+ const foundProperty = properties.find(property => property.key === itemKey || property.id === itemKey);
1865
1939
  if (!foundProperty) {
1866
1940
  throw new Error('Custom property mismatch');
1867
1941
  }
1868
1942
  return foundProperty;
1869
1943
  });
1870
- return orderedProperties;
1871
1944
  }
1872
1945
  async uploadFiles(model, moId) {
1873
1946
  const keys = Object.keys(model);
@@ -1899,36 +1972,37 @@ class AssetPropertiesComponent {
1899
1972
  }
1900
1973
  }
1901
1974
  setItemRequired(item, mo) {
1902
- const isAssetPropertyRequired = !!this.assetType?.c8y_IsAssetType?.properties.find(p => p.id === mo.id)?.isRequired;
1975
+ const isAssetPropertyRequired = this.assetType?.c8y_IsAssetType
1976
+ ? !!this.assetType.c8y_IsAssetType.properties.find(p => p.id === mo.id)?.isRequired
1977
+ : mo.isRequired;
1903
1978
  if (!isAssetPropertyRequired) {
1904
1979
  return;
1905
1980
  }
1906
1981
  const isComplexProperty = !!item?.complex?.length;
1982
+ const propertyKey = mo.c8y_JsonSchema?.key || mo.key;
1907
1983
  if (isComplexProperty) {
1908
- const complexProperty = item.jsonSchema?.properties?.[mo.c8y_JsonSchema.key];
1984
+ const complexProperty = item.jsonSchema?.properties?.[propertyKey];
1909
1985
  complexProperty.required = item.complex.map(({ key }) => key);
1910
1986
  }
1911
1987
  else {
1912
- item.jsonSchema.required = [mo.c8y_JsonSchema.key];
1988
+ item.jsonSchema.required = [propertyKey];
1913
1989
  }
1914
1990
  }
1915
1991
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertiesComponent, deps: [{ token: i3.AssetTypesRealtimeService }, { token: i2.InventoryService }, { token: i2.InventoryBinaryService }, { token: i3.AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
1916
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: AssetPropertiesComponent, isStandalone: true, selector: "c8y-asset-properties", inputs: { asset: "asset", properties: "properties" }, outputs: { assetChange: "assetChange" }, usesOnChanges: true, ngImport: i0, template: "<ng-container>\n <div class=\"card-header bg-inherit separator sticky-top\">\n <h1\n class=\"card-title p-t-4 p-b-4\"\n ngNonBindable\n translate\n [translateParams]=\"{ label: assetType?.label || '' | translate }\"\n >\n {{ label }} properties\n </h1>\n </div>\n <div class=\"card-block\">\n <div\n class=\"text-center\"\n *ngIf=\"isLoading\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <ng-container *ngIf=\"!isLoading\">\n <div\n class=\"card m-b-8\"\n title=\"{{ prop.description | translate }}\"\n *ngFor=\"let prop of customProperties\"\n [ngClass]=\"{ 'card-highlight': prop.isEdit }\"\n >\n <div\n class=\"card-block\"\n [ngClass]=\"{ 'p-b-0': prop.isEdit }\"\n >\n <div\n class=\"d-flex p-b-8 a-i-center\"\n *ngIf=\"!prop.isEdit\"\n >\n <p\n class=\"text-medium text-truncate\"\n title=\"{{ prop?.label | translate }}\"\n >\n {{ prop?.label | translate }}\n </p>\n <button\n class=\"btn btn-dot m-l-auto text-12\"\n [attr.aria-label]=\"'Edit' | translate\"\n tooltip=\"{{ 'Edit' | translate }}\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"toggleEdit(prop)\"\n >\n <i c8yIcon=\"pencil\"></i>\n </button>\n </div>\n <c8y-asset-properties-item\n #assetProps\n [file]=\"prop.file\"\n [key]=\"prop.key\"\n [type]=\"prop.type\"\n [value]=\"prop.value\"\n [complex]=\"prop.complex\"\n [isEdit]=\"prop.isEdit\"\n [jsonSchema]=\"prop.jsonSchema\"\n ></c8y-asset-properties-item>\n <div *ngIf=\"prop.key === POSITION_PROPERTY_KEY\">\n <c8y-asset-location\n [locationMO]=\"asset\"\n [isEdit]=\"prop.isEdit\"\n [form]=\"assetProps.form\"\n ></c8y-asset-location>\n </div>\n </div>\n <div\n class=\"card-footer p-t-0\"\n *ngIf=\"prop.isEdit\"\n >\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"toggleEdit(prop)\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-sm\"\n title=\"{{ 'Save' | translate }}\"\n type=\"button\"\n [disabled]=\"!assetProps?.form?.valid || !assetProps?.form?.dirty\"\n (click)=\"save(assetProps.form.value, prop)\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </ng-container>\n </div>\n</ng-container>\n", dependencies: [{ kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: 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: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: AssetPropertiesItemComponent, selector: "c8y-asset-properties-item", inputs: ["key", "value", "label", "type", "file", "complex", "isEdit", "jsonSchema"] }, { kind: "component", type: AssetLocationComponent, selector: "c8y-asset-location", inputs: ["isEdit", "locationMO", "form"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
1992
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AssetPropertiesComponent, isStandalone: true, selector: "c8y-asset-properties", inputs: { asset: "asset", properties: "properties" }, outputs: { assetChange: "assetChange" }, usesOnChanges: true, ngImport: i0, template: "<ng-container>\n <div class=\"card-header bg-inherit separator sticky-top\">\n <h1\n class=\"card-title p-t-4 p-b-4\"\n ngNonBindable\n translate\n [translateParams]=\"{\n label: assetType?.c8y_JsonSchema?.title || assetType?.label || '' | translate\n }\"\n >\n {{ label }} properties\n </h1>\n </div>\n <div class=\"card-block\">\n @if (isLoading) {\n <div class=\"text-center\">\n <c8y-loading></c8y-loading>\n </div>\n }\n\n @if (!isLoading) {\n @for (prop of customProperties; track prop.key) {\n <div\n class=\"card m-b-8\"\n title=\"{{ prop.description | translate }}\"\n [ngClass]=\"{ 'card-highlight': prop.isEdit }\"\n >\n <div\n class=\"card-block\"\n [ngClass]=\"{ 'p-b-0': prop.isEdit }\"\n >\n @if (!prop.isEdit) {\n <div class=\"d-flex p-b-8 a-i-center\">\n <p\n class=\"text-medium text-truncate\"\n title=\"{{ prop?.label | translate }}\"\n >\n {{ prop?.label | translate }}\n </p>\n <button\n class=\"btn btn-dot m-l-auto text-12\"\n [attr.aria-label]=\"'Edit' | translate\"\n tooltip=\"{{ 'Edit' | translate }}\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"toggleEdit(prop)\"\n >\n <i c8yIcon=\"pencil\"></i>\n </button>\n </div>\n }\n <c8y-asset-properties-item\n #assetProps\n [file]=\"prop.file\"\n [key]=\"prop.key\"\n [type]=\"prop.type\"\n [value]=\"prop.value\"\n [complex]=\"prop.complex\"\n [isEdit]=\"prop.isEdit\"\n [jsonSchema]=\"prop.jsonSchema\"\n ></c8y-asset-properties-item>\n @if (prop.key === POSITION_PROPERTY_KEY) {\n <div>\n <c8y-asset-location\n [locationMO]=\"asset\"\n [isEdit]=\"prop.isEdit\"\n [form]=\"assetProps.form\"\n ></c8y-asset-location>\n </div>\n }\n </div>\n @if (prop.isEdit) {\n <div class=\"card-footer p-t-0\">\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"toggleEdit(prop)\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-sm\"\n title=\"{{ 'Save' | translate }}\"\n type=\"button\"\n [disabled]=\"!assetProps?.form?.valid || !assetProps?.form?.dirty\"\n (click)=\"save(assetProps.form.value, prop)\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n }\n </div>\n }\n }\n </div>\n</ng-container>\n", dependencies: [{ kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: 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: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: AssetPropertiesItemComponent, selector: "c8y-asset-properties-item", inputs: ["key", "value", "label", "type", "file", "complex", "isEdit", "jsonSchema"] }, { kind: "component", type: AssetLocationComponent, selector: "c8y-asset-location", inputs: ["isEdit", "locationMO", "form"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
1917
1993
  }
1918
1994
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertiesComponent, decorators: [{
1919
1995
  type: Component,
1920
1996
  args: [{ selector: 'c8y-asset-properties', imports: [
1921
1997
  C8yTranslateDirective,
1922
- NgIf,
1923
1998
  LoadingComponent,
1924
- NgFor,
1925
1999
  NgClass,
1926
2000
  TooltipDirective,
1927
2001
  IconDirective,
1928
2002
  AssetPropertiesItemComponent,
1929
2003
  AssetLocationComponent,
1930
2004
  C8yTranslatePipe
1931
- ], template: "<ng-container>\n <div class=\"card-header bg-inherit separator sticky-top\">\n <h1\n class=\"card-title p-t-4 p-b-4\"\n ngNonBindable\n translate\n [translateParams]=\"{ label: assetType?.label || '' | translate }\"\n >\n {{ label }} properties\n </h1>\n </div>\n <div class=\"card-block\">\n <div\n class=\"text-center\"\n *ngIf=\"isLoading\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <ng-container *ngIf=\"!isLoading\">\n <div\n class=\"card m-b-8\"\n title=\"{{ prop.description | translate }}\"\n *ngFor=\"let prop of customProperties\"\n [ngClass]=\"{ 'card-highlight': prop.isEdit }\"\n >\n <div\n class=\"card-block\"\n [ngClass]=\"{ 'p-b-0': prop.isEdit }\"\n >\n <div\n class=\"d-flex p-b-8 a-i-center\"\n *ngIf=\"!prop.isEdit\"\n >\n <p\n class=\"text-medium text-truncate\"\n title=\"{{ prop?.label | translate }}\"\n >\n {{ prop?.label | translate }}\n </p>\n <button\n class=\"btn btn-dot m-l-auto text-12\"\n [attr.aria-label]=\"'Edit' | translate\"\n tooltip=\"{{ 'Edit' | translate }}\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"toggleEdit(prop)\"\n >\n <i c8yIcon=\"pencil\"></i>\n </button>\n </div>\n <c8y-asset-properties-item\n #assetProps\n [file]=\"prop.file\"\n [key]=\"prop.key\"\n [type]=\"prop.type\"\n [value]=\"prop.value\"\n [complex]=\"prop.complex\"\n [isEdit]=\"prop.isEdit\"\n [jsonSchema]=\"prop.jsonSchema\"\n ></c8y-asset-properties-item>\n <div *ngIf=\"prop.key === POSITION_PROPERTY_KEY\">\n <c8y-asset-location\n [locationMO]=\"asset\"\n [isEdit]=\"prop.isEdit\"\n [form]=\"assetProps.form\"\n ></c8y-asset-location>\n </div>\n </div>\n <div\n class=\"card-footer p-t-0\"\n *ngIf=\"prop.isEdit\"\n >\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"toggleEdit(prop)\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-sm\"\n title=\"{{ 'Save' | translate }}\"\n type=\"button\"\n [disabled]=\"!assetProps?.form?.valid || !assetProps?.form?.dirty\"\n (click)=\"save(assetProps.form.value, prop)\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </ng-container>\n </div>\n</ng-container>\n" }]
2005
+ ], template: "<ng-container>\n <div class=\"card-header bg-inherit separator sticky-top\">\n <h1\n class=\"card-title p-t-4 p-b-4\"\n ngNonBindable\n translate\n [translateParams]=\"{\n label: assetType?.c8y_JsonSchema?.title || assetType?.label || '' | translate\n }\"\n >\n {{ label }} properties\n </h1>\n </div>\n <div class=\"card-block\">\n @if (isLoading) {\n <div class=\"text-center\">\n <c8y-loading></c8y-loading>\n </div>\n }\n\n @if (!isLoading) {\n @for (prop of customProperties; track prop.key) {\n <div\n class=\"card m-b-8\"\n title=\"{{ prop.description | translate }}\"\n [ngClass]=\"{ 'card-highlight': prop.isEdit }\"\n >\n <div\n class=\"card-block\"\n [ngClass]=\"{ 'p-b-0': prop.isEdit }\"\n >\n @if (!prop.isEdit) {\n <div class=\"d-flex p-b-8 a-i-center\">\n <p\n class=\"text-medium text-truncate\"\n title=\"{{ prop?.label | translate }}\"\n >\n {{ prop?.label | translate }}\n </p>\n <button\n class=\"btn btn-dot m-l-auto text-12\"\n [attr.aria-label]=\"'Edit' | translate\"\n tooltip=\"{{ 'Edit' | translate }}\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"toggleEdit(prop)\"\n >\n <i c8yIcon=\"pencil\"></i>\n </button>\n </div>\n }\n <c8y-asset-properties-item\n #assetProps\n [file]=\"prop.file\"\n [key]=\"prop.key\"\n [type]=\"prop.type\"\n [value]=\"prop.value\"\n [complex]=\"prop.complex\"\n [isEdit]=\"prop.isEdit\"\n [jsonSchema]=\"prop.jsonSchema\"\n ></c8y-asset-properties-item>\n @if (prop.key === POSITION_PROPERTY_KEY) {\n <div>\n <c8y-asset-location\n [locationMO]=\"asset\"\n [isEdit]=\"prop.isEdit\"\n [form]=\"assetProps.form\"\n ></c8y-asset-location>\n </div>\n }\n </div>\n @if (prop.isEdit) {\n <div class=\"card-footer p-t-0\">\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"toggleEdit(prop)\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-sm\"\n title=\"{{ 'Save' | translate }}\"\n type=\"button\"\n [disabled]=\"!assetProps?.form?.valid || !assetProps?.form?.dirty\"\n (click)=\"save(assetProps.form.value, prop)\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n }\n </div>\n }\n }\n </div>\n</ng-container>\n" }]
1932
2006
  }], ctorParameters: () => [{ type: i3.AssetTypesRealtimeService }, { type: i2.InventoryService }, { type: i2.InventoryBinaryService }, { type: i3.AlertService }], propDecorators: { asset: [{
1933
2007
  type: Input
1934
2008
  }], assetChange: [{
@@ -1966,6 +2040,7 @@ class GroupInfoComponent {
1966
2040
  c8y_Notes: ''
1967
2041
  };
1968
2042
  this.filterMsg = gettext('Smart groups are groups dynamically constructed based on filtering criteria.');
2043
+ this.descriptionLabel = gettext('e.g. My description');
1969
2044
  this.GROUP_UPDATED_MSG = gettext('Group updated.');
1970
2045
  this.destroyed$ = new Subject();
1971
2046
  this.deviceListExtensionService.items$.pipe(takeUntil(this.destroyed$)).subscribe(columns => {
@@ -1986,9 +2061,13 @@ class GroupInfoComponent {
1986
2061
  externalFilterQuery: col.filter.externalFilterQuery,
1987
2062
  ...this.withPropsFromGridColumn(col)
1988
2063
  }));
1989
- this.label = this.assetNodeService.isAsset(this.group)
1990
- ? (await firstValueFrom(this.assetType.getAssetTypeByName$(this.group.type))).label
1991
- : gettext('Group');
2064
+ if (this.assetNodeService.isAsset(this.group)) {
2065
+ const assetType = await firstValueFrom(this.assetType.getAssetTypeByName$(this.group.type));
2066
+ this.label = assetType?.label || assetType?.c8y_JsonSchema?.title || gettext('Group');
2067
+ }
2068
+ else {
2069
+ this.label = gettext('Group');
2070
+ }
1992
2071
  }
1993
2072
  }
1994
2073
  isSmartGroup() {
@@ -2059,13 +2138,12 @@ class GroupInfoComponent {
2059
2138
  }
2060
2139
  }
2061
2140
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: GroupInfoComponent, deps: [{ token: i2.InventoryService }, { token: SubAssetsService }, { token: i2.SmartGroupsService }, { token: i3.AlertService }, { token: i3.ModalService }, { token: i4.AssetNodeService }, { token: i3.AssetTypesRealtimeService }, { token: i4$1.DeviceListExtensionService }, { token: SUB_ASSETS_CONFIG }], target: i0.ɵɵFactoryTarget.Component }); }
2062
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: GroupInfoComponent, isStandalone: true, selector: "c8y-group-info", inputs: { group: "group" }, outputs: { groupChange: "groupChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"bg-level-1 separator-bottom\">\n <div class=\"card-block p-t-24 p-b-24 large-padding\">\n <div class=\"content-flex-70\">\n <div class=\"text-center col-1\">\n <i\n class=\"c8y-icon-duocolor icon-48\"\n [c8yIcon]=\"groupIcon\"\n ></i>\n <p>\n <small\n class=\"label label-info\"\n *ngIf=\"group.c8y_IsDynamicGroup\"\n >\n {{ 'Smart group' | translate }}\n </small>\n <small\n class=\"label label-info text-truncate d-inline-block\"\n title=\"{{ label | translate }}\"\n *ngIf=\"!group.c8y_IsDynamicGroup && !group.com_cumulocity_model_Agent\"\n >\n {{ label | translate }}\n </small>\n <small\n class=\"label label-info\"\n *ngIf=\"group.com_cumulocity_model_Agent\"\n >\n {{ 'Remote group' | translate }}\n </small>\n </p>\n </div>\n\n <div class=\"flex-grow col-10\">\n <div class=\"content-flex-80\">\n <div class=\"col-9\">\n <form #groupNameForm=\"ngForm\">\n <c8y-form-group class=\"form-group-lg m-b-0\">\n <label\n class=\"sr-only\"\n for=\"groupName\"\n translate\n >\n Name\n </label>\n <p\n class=\"form-control-static\"\n *ngIf=\"!canEdit\"\n >\n {{ groupInfoModel.name }}\n </p>\n <div\n class=\"input-group input-group-lg input-group-editable\"\n *ngIf=\"canEdit\"\n >\n <input\n class=\"form-control\"\n title=\"{{ groupInfoModel.name }}\"\n id=\"groupName\"\n placeholder=\"{{ 'e.g. My group' | translate }}\"\n name=\"name\"\n type=\"text\"\n required\n [(ngModel)]=\"groupInfoModel.name\"\n size=\"{{ groupInfoModel.name.length + 2 }}\"\n maxlength=\"254\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n action: PRODUCT_EXPERIENCE.GROUP_INFO.ACTIONS.EDIT,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.NAME\n }\"\n />\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n [disabled]=\"groupNameForm.form.invalid\"\n (click)=\"\n update({ name: groupInfoModel.name }); groupNameForm.form.markAsPristine()\n \"\n [actionName]=\"'groupInfo:EditedNameSaved'\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n result: PRODUCT_EXPERIENCE.GROUP_INFO.RESULTS.EDIT_SAVED,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.NAME\n }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </c8y-form-group>\n </form>\n <form #groupDescriptionForm=\"ngForm\">\n <label\n class=\"sr-only\"\n for=\"description\"\n translate\n >\n Description\n </label>\n <p\n class=\"form-control-static\"\n *ngIf=\"!canEdit\"\n >\n {{ groupInfoModel.c8y_Notes }}\n </p>\n <div\n class=\"input-group input-group-editable\"\n *ngIf=\"canEdit\"\n >\n <textarea\n class=\"form-control no-resize\"\n title=\"{{\n groupInfoModel.c8y_Notes\n ? groupInfoModel.c8y_Notes\n : ('e.g. My description' | translate)\n }}\"\n id=\"description\"\n placeholder=\"{{ 'e.g. My description' | translate }}\"\n name=\"description\"\n c8y-textarea-autoresize\n [(ngModel)]=\"groupInfoModel.c8y_Notes\"\n cols=\"{{ groupInfoModel.c8y_Notes ? groupInfoModel.c8y_Notes.length : 25 }}\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n action: PRODUCT_EXPERIENCE.GROUP_INFO.ACTIONS.EDIT,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.DESCRIPTION\n }\"\n ></textarea>\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n [disabled]=\"groupDescriptionForm.form.invalid\"\n (click)=\"\n update({ c8y_Notes: groupInfoModel.c8y_Notes });\n groupDescriptionForm.form.markAsPristine()\n \"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n result: PRODUCT_EXPERIENCE.GROUP_INFO.RESULTS.EDIT_SAVED,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.DESCRIPTION\n }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </form>\n\n <div\n class=\"dropdown m-t-8\"\n placement=\"bottom left\"\n container=\"body\"\n type=\"button\"\n dropdown\n *ngIf=\"isSmartGroup()\"\n #ddFilters=\"bs-dropdown\"\n [insideClick]=\"true\"\n >\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Smart group filters' | translate }}\"\n aria-haspopup=\"true\"\n dropdownToggle\n data-cy=\"c8y-data-grid--filters\"\n [disabled]=\"columnsWithFilter?.length === 0\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"filter\"\n ></i>\n <span>{{ 'Smart group filters' | translate }}</span>\n <span\n class=\"p-relative p-l-4 p-r-16\"\n *ngIf=\"columnsWithFilter?.length > 0\"\n >\n <span class=\"badge badge-system p-absolute\" data-cy=\"group-info--filter-number\">\n {{ columnsWithFilter?.length }}\n </span>\n </span>\n </button>\n <button\n class=\"btn-help btn-help--sm m-r-4\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ filterMsg | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n data-cy=\"group-info--help-button\"\n container=\"body\"\n type=\"button\"\n ></button>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"data-grid__dropdown bg-level-0\">\n <ul class=\"list-unstyled m-0\">\n <li\n *ngFor=\"let column of columnsWithFilter; let last = last\"\n [ngClass]=\"{ 'separator-bottom': !last }\"\n >\n <ng-container>\n <div\n class=\"dropdown-header sticky-top text-truncate no-border-top p-b-0\"\n title=\"{{ (column.header | translate) || column.name }}\"\n >\n <label>\n {{ (column.header | translate) || column.name }}\n </label>\n </div>\n <div\n class=\"list-group-item borderless d-flex d-col\"\n *ngFor=\"\n let groupedFilterChips of column\n | mapToFilterChips\n | async\n | groupedFilterChips;\n let first = first\n \"\n [ngClass]=\"{ 'p-t-0': first }\"\n >\n <p\n class=\"small p-b-4\"\n *ngIf=\"groupedFilterChips.label\"\n >\n {{ groupedFilterChips.label | translate }}\n </p>\n <div class=\"d-flex a-i-center gap-4 flex-wrap\">\n <span\n class=\"tag tag--info chip\"\n data-cy=\"group-info--grouped-filter-chip\"\n *ngFor=\"let chip of groupedFilterChips.chips\"\n >\n {{ chip.displayValue | translate }}\n </span>\n </div>\n </div>\n </ng-container>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div class=\"flex-grow\">\n <ul class=\"list-unstyled small\">\n <li class=\"p-t-4 p-b-4 d-flex separator-top-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-8\">{{ 'Created' | translate }}</label>\n <span class=\"m-l-auto\">{{ group.creationTime | c8yDate }}</span>\n </li>\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-8\">{{ 'Last updated' | translate }}</label>\n <span class=\"m-l-auto\">{{ group.lastUpdated | c8yDate }}</span>\n </li>\n <li\n class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\"\n *ngIf=\"group.com_cumulocity_model_Agent\"\n >\n <label class=\"small m-b-0 m-r-8\">{{ 'Status' | translate }}</label>\n <span\n class=\"m-l-auto\"\n *ngIf=\"group.c8y_BrokerSource\"\n >\n {{ group.c8y_BrokerSource.status }}\n </span>\n <span\n class=\"m-l-auto\"\n *ngIf=\"!group.c8y_BrokerSource\"\n >\n {{ 'Offline' | translate }}\n </span>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "directive", type: TextareaAutoresizeDirective, selector: "[c8y-textarea-autoresize]" }, { kind: "directive", type: BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "directive", type: BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "directive", type: BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }, { kind: "pipe", type: FilterMapperPipe, name: "mapToFilterChips" }, { kind: "pipe", type: GroupedFilterChips, name: "groupedFilterChips" }] }); }
2141
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: GroupInfoComponent, isStandalone: true, selector: "c8y-group-info", inputs: { group: "group" }, outputs: { groupChange: "groupChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"bg-level-1 separator-bottom\">\n <div class=\"card-block p-t-24 p-b-24 large-padding\">\n <div class=\"content-flex-70\">\n <div class=\"text-center col-1\">\n <i\n class=\"c8y-icon-duocolor icon-48\"\n [c8yIcon]=\"groupIcon\"\n ></i>\n <p>\n @if (group.c8y_IsDynamicGroup) {\n <small class=\"label label-info\">\n {{ 'Smart group' | translate }}\n </small>\n }\n @if (!group.c8y_IsDynamicGroup && !group.com_cumulocity_model_Agent) {\n <small\n class=\"label label-info text-truncate d-inline-block\"\n title=\"{{ label | translate }}\"\n >\n {{ label | translate }}\n </small>\n }\n @if (group.com_cumulocity_model_Agent) {\n <small class=\"label label-info\">\n {{ 'Remote group' | translate }}\n </small>\n }\n </p>\n </div>\n\n <div class=\"flex-grow col-10\">\n <div class=\"content-flex-80\">\n <div class=\"col-9\">\n <form #groupNameForm=\"ngForm\">\n <c8y-form-group class=\"form-group-lg m-b-0\">\n <label\n class=\"sr-only\"\n for=\"groupName\"\n translate\n >\n Name\n </label>\n @if (!canEdit) {\n <p class=\"form-control-static\">\n {{ groupInfoModel.name }}\n </p>\n }\n @if (canEdit) {\n <div class=\"input-group input-group-lg input-group-editable\">\n <input\n class=\"form-control\"\n title=\"{{ groupInfoModel.name }}\"\n id=\"groupName\"\n placeholder=\"{{ 'e.g. My group' | translate }}\"\n name=\"name\"\n type=\"text\"\n required\n [(ngModel)]=\"groupInfoModel.name\"\n size=\"{{ groupInfoModel.name.length + 2 }}\"\n maxlength=\"254\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n action: PRODUCT_EXPERIENCE.GROUP_INFO.ACTIONS.EDIT,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.NAME\n }\"\n />\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n [disabled]=\"groupNameForm.form.invalid\"\n (click)=\"\n update({ name: groupInfoModel.name }); groupNameForm.form.markAsPristine()\n \"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n result: PRODUCT_EXPERIENCE.GROUP_INFO.RESULTS.EDIT_SAVED,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.NAME\n }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n }\n </c8y-form-group>\n </form>\n <form #groupDescriptionForm=\"ngForm\">\n <label\n class=\"sr-only\"\n for=\"description\"\n translate\n >\n Description\n </label>\n @if (canEdit) {\n <div class=\"input-group input-group-editable\">\n <textarea\n class=\"form-control no-resize\"\n title=\"{{\n groupInfoModel.c8y_Notes\n ? groupInfoModel.c8y_Notes\n : (descriptionLabel | translate)\n }}\"\n id=\"description\"\n placeholder=\"{{ descriptionLabel | translate }}\"\n name=\"description\"\n c8y-textarea-autoresize\n [(ngModel)]=\"groupInfoModel.c8y_Notes\"\n cols=\"{{ groupInfoModel.c8y_Notes ? groupInfoModel.c8y_Notes.length : 25 }}\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n action: PRODUCT_EXPERIENCE.GROUP_INFO.ACTIONS.EDIT,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.DESCRIPTION\n }\"\n ></textarea>\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n [disabled]=\"groupDescriptionForm.form.invalid\"\n (click)=\"\n update({ c8y_Notes: groupInfoModel.c8y_Notes });\n groupDescriptionForm.form.markAsPristine()\n \"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n result: PRODUCT_EXPERIENCE.GROUP_INFO.RESULTS.EDIT_SAVED,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.DESCRIPTION\n }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n } @else {\n <p class=\"form-control-static\">\n {{ groupInfoModel.c8y_Notes }}\n </p>\n }\n </form>\n\n @if (isSmartGroup()) {\n <div\n class=\"dropdown m-t-8\"\n placement=\"bottom left\"\n container=\"body\"\n type=\"button\"\n dropdown\n #ddFilters=\"bs-dropdown\"\n [insideClick]=\"true\"\n >\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Smart group filters' | translate }}\"\n aria-haspopup=\"true\"\n dropdownToggle\n data-cy=\"c8y-data-grid--filters\"\n [disabled]=\"columnsWithFilter?.length === 0\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"filter\"\n ></i>\n <span>{{ 'Smart group filters' | translate }}</span>\n @if (columnsWithFilter?.length > 0) {\n <span class=\"p-relative p-l-4 p-r-16\">\n <span\n class=\"badge badge-system p-absolute\"\n data-cy=\"group-info--filter-number\"\n >\n {{ columnsWithFilter?.length }}\n </span>\n </span>\n }\n </button>\n <button\n class=\"btn-help btn-help--sm m-r-4\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ filterMsg | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n data-cy=\"group-info--help-button\"\n ></button>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"data-grid__dropdown bg-level-0\">\n <ul class=\"list-unstyled m-0\">\n @for (column of columnsWithFilter; track column; let last = $last) {\n <li [ngClass]=\"{ 'separator-bottom': !last }\">\n <div\n class=\"dropdown-header sticky-top text-truncate no-border-top p-b-0\"\n title=\"{{ (column.header | translate) || column.name }}\"\n >\n <label>\n {{ (column.header | translate) || column.name }}\n </label>\n </div>\n @for (\n groupedFilterChips of column\n | mapToFilterChips\n | async\n | groupedFilterChips;\n track groupedFilterChips;\n let first = $first\n ) {\n <div\n class=\"list-group-item borderless d-flex d-col\"\n [ngClass]=\"{ 'p-t-0': first }\"\n >\n @if (groupedFilterChips.label) {\n <p class=\"small p-b-4\">\n {{ groupedFilterChips.label | translate }}\n </p>\n }\n <div class=\"d-flex a-i-center gap-4 flex-wrap\">\n @for (chip of groupedFilterChips.chips; track chip) {\n <span\n class=\"tag tag--info chip\"\n data-cy=\"group-info--grouped-filter-chip\"\n >\n {{ chip.displayValue | translate }}\n </span>\n }\n </div>\n </div>\n }\n </li>\n }\n </ul>\n </div>\n </div>\n </div>\n }\n </div>\n <div class=\"flex-grow\">\n <ul class=\"list-unstyled small\">\n <li class=\"p-t-4 p-b-4 d-flex separator-top-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-8\">{{ 'Created' | translate }}</label>\n <span class=\"m-l-auto\">{{ group.creationTime | c8yDate }}</span>\n </li>\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-8\">{{ 'Last updated' | translate }}</label>\n <span class=\"m-l-auto\">{{ group.lastUpdated | c8yDate }}</span>\n </li>\n @if (group.com_cumulocity_model_Agent) {\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-8\">{{ 'Status' | translate }}</label>\n @if (group.c8y_BrokerSource) {\n <span class=\"m-l-auto\">\n {{ group.c8y_BrokerSource.status | translate }}\n </span>\n }\n @if (!group.c8y_BrokerSource) {\n <span class=\"m-l-auto\">\n {{ 'Offline' | translate }}\n </span>\n }\n </li>\n }\n </ul>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "directive", type: TextareaAutoresizeDirective, selector: "[c8y-textarea-autoresize]" }, { kind: "directive", type: BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "directive", type: BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "directive", type: BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }, { kind: "pipe", type: FilterMapperPipe, name: "mapToFilterChips" }, { kind: "pipe", type: GroupedFilterChips, name: "groupedFilterChips" }] }); }
2063
2142
  }
2064
2143
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: GroupInfoComponent, decorators: [{
2065
2144
  type: Component,
2066
2145
  args: [{ selector: 'c8y-group-info', imports: [
2067
2146
  IconDirective,
2068
- NgIf,
2069
2147
  FormsModule,
2070
2148
  FormGroupComponent,
2071
2149
  C8yTranslateDirective,
@@ -2076,14 +2154,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2076
2154
  BsDropdownToggleDirective,
2077
2155
  PopoverDirective,
2078
2156
  BsDropdownMenuDirective,
2079
- NgFor,
2080
2157
  NgClass,
2081
2158
  C8yTranslatePipe,
2082
2159
  AsyncPipe,
2083
2160
  DatePipe,
2084
2161
  FilterMapperPipe,
2085
2162
  GroupedFilterChips
2086
- ], template: "<div class=\"bg-level-1 separator-bottom\">\n <div class=\"card-block p-t-24 p-b-24 large-padding\">\n <div class=\"content-flex-70\">\n <div class=\"text-center col-1\">\n <i\n class=\"c8y-icon-duocolor icon-48\"\n [c8yIcon]=\"groupIcon\"\n ></i>\n <p>\n <small\n class=\"label label-info\"\n *ngIf=\"group.c8y_IsDynamicGroup\"\n >\n {{ 'Smart group' | translate }}\n </small>\n <small\n class=\"label label-info text-truncate d-inline-block\"\n title=\"{{ label | translate }}\"\n *ngIf=\"!group.c8y_IsDynamicGroup && !group.com_cumulocity_model_Agent\"\n >\n {{ label | translate }}\n </small>\n <small\n class=\"label label-info\"\n *ngIf=\"group.com_cumulocity_model_Agent\"\n >\n {{ 'Remote group' | translate }}\n </small>\n </p>\n </div>\n\n <div class=\"flex-grow col-10\">\n <div class=\"content-flex-80\">\n <div class=\"col-9\">\n <form #groupNameForm=\"ngForm\">\n <c8y-form-group class=\"form-group-lg m-b-0\">\n <label\n class=\"sr-only\"\n for=\"groupName\"\n translate\n >\n Name\n </label>\n <p\n class=\"form-control-static\"\n *ngIf=\"!canEdit\"\n >\n {{ groupInfoModel.name }}\n </p>\n <div\n class=\"input-group input-group-lg input-group-editable\"\n *ngIf=\"canEdit\"\n >\n <input\n class=\"form-control\"\n title=\"{{ groupInfoModel.name }}\"\n id=\"groupName\"\n placeholder=\"{{ 'e.g. My group' | translate }}\"\n name=\"name\"\n type=\"text\"\n required\n [(ngModel)]=\"groupInfoModel.name\"\n size=\"{{ groupInfoModel.name.length + 2 }}\"\n maxlength=\"254\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n action: PRODUCT_EXPERIENCE.GROUP_INFO.ACTIONS.EDIT,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.NAME\n }\"\n />\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n [disabled]=\"groupNameForm.form.invalid\"\n (click)=\"\n update({ name: groupInfoModel.name }); groupNameForm.form.markAsPristine()\n \"\n [actionName]=\"'groupInfo:EditedNameSaved'\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n result: PRODUCT_EXPERIENCE.GROUP_INFO.RESULTS.EDIT_SAVED,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.NAME\n }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </c8y-form-group>\n </form>\n <form #groupDescriptionForm=\"ngForm\">\n <label\n class=\"sr-only\"\n for=\"description\"\n translate\n >\n Description\n </label>\n <p\n class=\"form-control-static\"\n *ngIf=\"!canEdit\"\n >\n {{ groupInfoModel.c8y_Notes }}\n </p>\n <div\n class=\"input-group input-group-editable\"\n *ngIf=\"canEdit\"\n >\n <textarea\n class=\"form-control no-resize\"\n title=\"{{\n groupInfoModel.c8y_Notes\n ? groupInfoModel.c8y_Notes\n : ('e.g. My description' | translate)\n }}\"\n id=\"description\"\n placeholder=\"{{ 'e.g. My description' | translate }}\"\n name=\"description\"\n c8y-textarea-autoresize\n [(ngModel)]=\"groupInfoModel.c8y_Notes\"\n cols=\"{{ groupInfoModel.c8y_Notes ? groupInfoModel.c8y_Notes.length : 25 }}\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n action: PRODUCT_EXPERIENCE.GROUP_INFO.ACTIONS.EDIT,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.DESCRIPTION\n }\"\n ></textarea>\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n [disabled]=\"groupDescriptionForm.form.invalid\"\n (click)=\"\n update({ c8y_Notes: groupInfoModel.c8y_Notes });\n groupDescriptionForm.form.markAsPristine()\n \"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n result: PRODUCT_EXPERIENCE.GROUP_INFO.RESULTS.EDIT_SAVED,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.DESCRIPTION\n }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </form>\n\n <div\n class=\"dropdown m-t-8\"\n placement=\"bottom left\"\n container=\"body\"\n type=\"button\"\n dropdown\n *ngIf=\"isSmartGroup()\"\n #ddFilters=\"bs-dropdown\"\n [insideClick]=\"true\"\n >\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Smart group filters' | translate }}\"\n aria-haspopup=\"true\"\n dropdownToggle\n data-cy=\"c8y-data-grid--filters\"\n [disabled]=\"columnsWithFilter?.length === 0\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"filter\"\n ></i>\n <span>{{ 'Smart group filters' | translate }}</span>\n <span\n class=\"p-relative p-l-4 p-r-16\"\n *ngIf=\"columnsWithFilter?.length > 0\"\n >\n <span class=\"badge badge-system p-absolute\" data-cy=\"group-info--filter-number\">\n {{ columnsWithFilter?.length }}\n </span>\n </span>\n </button>\n <button\n class=\"btn-help btn-help--sm m-r-4\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ filterMsg | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n data-cy=\"group-info--help-button\"\n container=\"body\"\n type=\"button\"\n ></button>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"data-grid__dropdown bg-level-0\">\n <ul class=\"list-unstyled m-0\">\n <li\n *ngFor=\"let column of columnsWithFilter; let last = last\"\n [ngClass]=\"{ 'separator-bottom': !last }\"\n >\n <ng-container>\n <div\n class=\"dropdown-header sticky-top text-truncate no-border-top p-b-0\"\n title=\"{{ (column.header | translate) || column.name }}\"\n >\n <label>\n {{ (column.header | translate) || column.name }}\n </label>\n </div>\n <div\n class=\"list-group-item borderless d-flex d-col\"\n *ngFor=\"\n let groupedFilterChips of column\n | mapToFilterChips\n | async\n | groupedFilterChips;\n let first = first\n \"\n [ngClass]=\"{ 'p-t-0': first }\"\n >\n <p\n class=\"small p-b-4\"\n *ngIf=\"groupedFilterChips.label\"\n >\n {{ groupedFilterChips.label | translate }}\n </p>\n <div class=\"d-flex a-i-center gap-4 flex-wrap\">\n <span\n class=\"tag tag--info chip\"\n data-cy=\"group-info--grouped-filter-chip\"\n *ngFor=\"let chip of groupedFilterChips.chips\"\n >\n {{ chip.displayValue | translate }}\n </span>\n </div>\n </div>\n </ng-container>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div class=\"flex-grow\">\n <ul class=\"list-unstyled small\">\n <li class=\"p-t-4 p-b-4 d-flex separator-top-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-8\">{{ 'Created' | translate }}</label>\n <span class=\"m-l-auto\">{{ group.creationTime | c8yDate }}</span>\n </li>\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-8\">{{ 'Last updated' | translate }}</label>\n <span class=\"m-l-auto\">{{ group.lastUpdated | c8yDate }}</span>\n </li>\n <li\n class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\"\n *ngIf=\"group.com_cumulocity_model_Agent\"\n >\n <label class=\"small m-b-0 m-r-8\">{{ 'Status' | translate }}</label>\n <span\n class=\"m-l-auto\"\n *ngIf=\"group.c8y_BrokerSource\"\n >\n {{ group.c8y_BrokerSource.status }}\n </span>\n <span\n class=\"m-l-auto\"\n *ngIf=\"!group.c8y_BrokerSource\"\n >\n {{ 'Offline' | translate }}\n </span>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n" }]
2163
+ ], template: "<div class=\"bg-level-1 separator-bottom\">\n <div class=\"card-block p-t-24 p-b-24 large-padding\">\n <div class=\"content-flex-70\">\n <div class=\"text-center col-1\">\n <i\n class=\"c8y-icon-duocolor icon-48\"\n [c8yIcon]=\"groupIcon\"\n ></i>\n <p>\n @if (group.c8y_IsDynamicGroup) {\n <small class=\"label label-info\">\n {{ 'Smart group' | translate }}\n </small>\n }\n @if (!group.c8y_IsDynamicGroup && !group.com_cumulocity_model_Agent) {\n <small\n class=\"label label-info text-truncate d-inline-block\"\n title=\"{{ label | translate }}\"\n >\n {{ label | translate }}\n </small>\n }\n @if (group.com_cumulocity_model_Agent) {\n <small class=\"label label-info\">\n {{ 'Remote group' | translate }}\n </small>\n }\n </p>\n </div>\n\n <div class=\"flex-grow col-10\">\n <div class=\"content-flex-80\">\n <div class=\"col-9\">\n <form #groupNameForm=\"ngForm\">\n <c8y-form-group class=\"form-group-lg m-b-0\">\n <label\n class=\"sr-only\"\n for=\"groupName\"\n translate\n >\n Name\n </label>\n @if (!canEdit) {\n <p class=\"form-control-static\">\n {{ groupInfoModel.name }}\n </p>\n }\n @if (canEdit) {\n <div class=\"input-group input-group-lg input-group-editable\">\n <input\n class=\"form-control\"\n title=\"{{ groupInfoModel.name }}\"\n id=\"groupName\"\n placeholder=\"{{ 'e.g. My group' | translate }}\"\n name=\"name\"\n type=\"text\"\n required\n [(ngModel)]=\"groupInfoModel.name\"\n size=\"{{ groupInfoModel.name.length + 2 }}\"\n maxlength=\"254\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n action: PRODUCT_EXPERIENCE.GROUP_INFO.ACTIONS.EDIT,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.NAME\n }\"\n />\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n [disabled]=\"groupNameForm.form.invalid\"\n (click)=\"\n update({ name: groupInfoModel.name }); groupNameForm.form.markAsPristine()\n \"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n result: PRODUCT_EXPERIENCE.GROUP_INFO.RESULTS.EDIT_SAVED,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.NAME\n }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n }\n </c8y-form-group>\n </form>\n <form #groupDescriptionForm=\"ngForm\">\n <label\n class=\"sr-only\"\n for=\"description\"\n translate\n >\n Description\n </label>\n @if (canEdit) {\n <div class=\"input-group input-group-editable\">\n <textarea\n class=\"form-control no-resize\"\n title=\"{{\n groupInfoModel.c8y_Notes\n ? groupInfoModel.c8y_Notes\n : (descriptionLabel | translate)\n }}\"\n id=\"description\"\n placeholder=\"{{ descriptionLabel | translate }}\"\n name=\"description\"\n c8y-textarea-autoresize\n [(ngModel)]=\"groupInfoModel.c8y_Notes\"\n cols=\"{{ groupInfoModel.c8y_Notes ? groupInfoModel.c8y_Notes.length : 25 }}\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n action: PRODUCT_EXPERIENCE.GROUP_INFO.ACTIONS.EDIT,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.DESCRIPTION\n }\"\n ></textarea>\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n [disabled]=\"groupDescriptionForm.form.invalid\"\n (click)=\"\n update({ c8y_Notes: groupInfoModel.c8y_Notes });\n groupDescriptionForm.form.markAsPristine()\n \"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.GROUP_INFO.COMPONENTS.GROUP_INFO,\n result: PRODUCT_EXPERIENCE.GROUP_INFO.RESULTS.EDIT_SAVED,\n property: PRODUCT_EXPERIENCE.GROUP_INFO.PROPERTIES.DESCRIPTION\n }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n } @else {\n <p class=\"form-control-static\">\n {{ groupInfoModel.c8y_Notes }}\n </p>\n }\n </form>\n\n @if (isSmartGroup()) {\n <div\n class=\"dropdown m-t-8\"\n placement=\"bottom left\"\n container=\"body\"\n type=\"button\"\n dropdown\n #ddFilters=\"bs-dropdown\"\n [insideClick]=\"true\"\n >\n <button\n class=\"btn btn-default btn-sm\"\n title=\"{{ 'Smart group filters' | translate }}\"\n aria-haspopup=\"true\"\n dropdownToggle\n data-cy=\"c8y-data-grid--filters\"\n [disabled]=\"columnsWithFilter?.length === 0\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"filter\"\n ></i>\n <span>{{ 'Smart group filters' | translate }}</span>\n @if (columnsWithFilter?.length > 0) {\n <span class=\"p-relative p-l-4 p-r-16\">\n <span\n class=\"badge badge-system p-absolute\"\n data-cy=\"group-info--filter-number\"\n >\n {{ columnsWithFilter?.length }}\n </span>\n </span>\n }\n </button>\n <button\n class=\"btn-help btn-help--sm m-r-4\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ filterMsg | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n data-cy=\"group-info--help-button\"\n ></button>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"data-grid__dropdown bg-level-0\">\n <ul class=\"list-unstyled m-0\">\n @for (column of columnsWithFilter; track column; let last = $last) {\n <li [ngClass]=\"{ 'separator-bottom': !last }\">\n <div\n class=\"dropdown-header sticky-top text-truncate no-border-top p-b-0\"\n title=\"{{ (column.header | translate) || column.name }}\"\n >\n <label>\n {{ (column.header | translate) || column.name }}\n </label>\n </div>\n @for (\n groupedFilterChips of column\n | mapToFilterChips\n | async\n | groupedFilterChips;\n track groupedFilterChips;\n let first = $first\n ) {\n <div\n class=\"list-group-item borderless d-flex d-col\"\n [ngClass]=\"{ 'p-t-0': first }\"\n >\n @if (groupedFilterChips.label) {\n <p class=\"small p-b-4\">\n {{ groupedFilterChips.label | translate }}\n </p>\n }\n <div class=\"d-flex a-i-center gap-4 flex-wrap\">\n @for (chip of groupedFilterChips.chips; track chip) {\n <span\n class=\"tag tag--info chip\"\n data-cy=\"group-info--grouped-filter-chip\"\n >\n {{ chip.displayValue | translate }}\n </span>\n }\n </div>\n </div>\n }\n </li>\n }\n </ul>\n </div>\n </div>\n </div>\n }\n </div>\n <div class=\"flex-grow\">\n <ul class=\"list-unstyled small\">\n <li class=\"p-t-4 p-b-4 d-flex separator-top-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-8\">{{ 'Created' | translate }}</label>\n <span class=\"m-l-auto\">{{ group.creationTime | c8yDate }}</span>\n </li>\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-8\">{{ 'Last updated' | translate }}</label>\n <span class=\"m-l-auto\">{{ group.lastUpdated | c8yDate }}</span>\n </li>\n @if (group.com_cumulocity_model_Agent) {\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-8\">{{ 'Status' | translate }}</label>\n @if (group.c8y_BrokerSource) {\n <span class=\"m-l-auto\">\n {{ group.c8y_BrokerSource.status | translate }}\n </span>\n }\n @if (!group.c8y_BrokerSource) {\n <span class=\"m-l-auto\">\n {{ 'Offline' | translate }}\n </span>\n }\n </li>\n }\n </ul>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n" }]
2087
2164
  }], ctorParameters: () => [{ type: i2.InventoryService }, { type: SubAssetsService }, { type: i2.SmartGroupsService }, { type: i3.AlertService }, { type: i3.ModalService }, { type: i4.AssetNodeService }, { type: i3.AssetTypesRealtimeService }, { type: i4$1.DeviceListExtensionService }, { type: undefined, decorators: [{
2088
2165
  type: Inject,
2089
2166
  args: [SUB_ASSETS_CONFIG]