@c8y/ngx-components 1021.58.0 → 1021.59.6

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 (56) hide show
  1. package/core/router/scoped-context-route.service.d.ts +2 -1
  2. package/core/router/scoped-context-route.service.d.ts.map +1 -1
  3. package/device-provisioned-certificates/device-tab-provisioned-certificates.component.d.ts.map +1 -1
  4. package/esm2022/core/router/scoped-context-route.service.mjs +32 -33
  5. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.component.mjs +3 -3
  6. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-preview/datapoints-export-selector-preview.component.mjs +3 -3
  7. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-exports-selector-data-scope/datapoints-exports-selector-data-scope.component.mjs +3 -3
  8. package/esm2022/datapoints-export-selector/datapoints-export-selector.component.mjs +3 -3
  9. package/esm2022/device-provisioned-certificates/device-provisioned-certificates.guard.mjs +2 -2
  10. package/esm2022/device-provisioned-certificates/device-tab-provisioned-certificates.component.mjs +7 -5
  11. package/esm2022/sensor-phone/sensor-phone-modal/sensor-phone-modal.component.mjs +4 -4
  12. package/esm2022/sub-assets/add-group/add-group.component.mjs +30 -4
  13. package/esm2022/sub-assets/add-group/add-group.module.mjs +23 -4
  14. package/esm2022/sub-assets/assign-devices/assign-child-devices.component.mjs +14 -2
  15. package/esm2022/sub-assets/groups.component.mjs +3 -3
  16. package/esm2022/sub-assets/sub-assets-grids.module.mjs +24 -0
  17. package/esm2022/sub-assets/sub-assets.component.mjs +4 -4
  18. package/esm2022/sub-assets/sub-assets.module.mjs +8 -12
  19. package/esm2022/trusted-certificates/crl/crl-settings.component.mjs +3 -3
  20. package/esm2022/trusted-certificates/list/trusted-certificate-list.component.mjs +5 -5
  21. package/fesm2022/c8y-ngx-components-datapoints-export-selector.mjs +8 -8
  22. package/fesm2022/c8y-ngx-components-datapoints-export-selector.mjs.map +1 -1
  23. package/fesm2022/c8y-ngx-components-device-provisioned-certificates.mjs +7 -5
  24. package/fesm2022/c8y-ngx-components-device-provisioned-certificates.mjs.map +1 -1
  25. package/fesm2022/c8y-ngx-components-sensor-phone-sensor-phone-modal.mjs +3 -3
  26. package/fesm2022/c8y-ngx-components-sensor-phone-sensor-phone-modal.mjs.map +1 -1
  27. package/fesm2022/c8y-ngx-components-sub-assets.mjs +1246 -1181
  28. package/fesm2022/c8y-ngx-components-sub-assets.mjs.map +1 -1
  29. package/fesm2022/c8y-ngx-components-trusted-certificates.mjs +6 -6
  30. package/fesm2022/c8y-ngx-components-trusted-certificates.mjs.map +1 -1
  31. package/fesm2022/c8y-ngx-components.mjs +29 -30
  32. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  33. package/locales/de.po +46 -51
  34. package/locales/es.po +25 -31
  35. package/locales/fr.po +30 -35
  36. package/locales/ja_JP.po +24 -32
  37. package/locales/ko.po +26 -31
  38. package/locales/locales.pot +6 -6
  39. package/locales/nl.po +26 -31
  40. package/locales/pl.po +27 -32
  41. package/locales/pt_BR.po +25 -30
  42. package/locales/zh_CN.po +27 -32
  43. package/locales/zh_TW.po +28 -33
  44. package/package.json +1 -1
  45. package/sensor-phone/sensor-phone-modal/sensor-phone-modal.component.d.ts.map +1 -1
  46. package/sub-assets/add-group/add-group.component.d.ts +9 -1
  47. package/sub-assets/add-group/add-group.component.d.ts.map +1 -1
  48. package/sub-assets/add-group/add-group.module.d.ts +3 -1
  49. package/sub-assets/add-group/add-group.module.d.ts.map +1 -1
  50. package/sub-assets/assign-devices/assign-child-devices.component.d.ts +3 -1
  51. package/sub-assets/assign-devices/assign-child-devices.component.d.ts.map +1 -1
  52. package/sub-assets/sub-assets-grids.module.d.ts +15 -0
  53. package/sub-assets/sub-assets-grids.module.d.ts.map +1 -0
  54. package/sub-assets/sub-assets.module.d.ts +18 -19
  55. package/sub-assets/sub-assets.module.d.ts.map +1 -1
  56. package/trusted-certificates/list/trusted-certificate-list.component.d.ts.map +1 -1
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, EventEmitter, Component, Input, Output, ViewChild, HostListener, NgModule, Inject, Optional, TemplateRef, InjectionToken, signal } from '@angular/core';
2
+ import { Injectable, Component, Input, ViewChild, Inject, Optional, EventEmitter, Output, HostListener, TemplateRef, NgModule, InjectionToken, signal } from '@angular/core';
3
3
  import * as i3 from '@c8y/ngx-components';
4
- import { DataGridService, gettext, Permissions, GroupFragment, C8yStepper, CoreModule, Status, AbstractConfigurationStrategy, DATA_GRID_CONFIGURATION_CONTEXT, DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER, BuiltInActionType, UserPreferencesConfigurationStrategy, DATA_GRID_CONFIGURATION_STRATEGY, DataGridComponent, CustomColumn, FilterMapperModule, hookRoute, ViewContext } from '@c8y/ngx-components';
4
+ import { DataGridService, gettext, Permissions, GroupFragment, Status, AbstractConfigurationStrategy, DATA_GRID_CONFIGURATION_CONTEXT, DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER, BuiltInActionType, UserPreferencesConfigurationStrategy, DATA_GRID_CONFIGURATION_STRATEGY, DataGridComponent, C8yStepper, CoreModule, CustomColumn, FilterMapperModule, hookRoute, ViewContext } from '@c8y/ngx-components';
5
5
  import * as i7 from '@c8y/ngx-components/device-grid';
6
6
  import { NameDeviceGridColumn, ModelDeviceGridColumn, SerialNumberDeviceGridColumn, RegistrationDateDeviceGridColumn, SystemIdDeviceGridColumn, ImeiDeviceGridColumn, AlarmsDeviceGridColumn, DeviceGridModule } from '@c8y/ngx-components/device-grid';
7
7
  import * as i1$1 from '@angular/forms';
@@ -13,20 +13,20 @@ import { AssetTypeGridColumn, AssetTypeCellRendererComponent } from '@c8y/ngx-co
13
13
  import { firstValueFrom, Subject, of, delay, takeUntil as takeUntil$1, tap } from 'rxjs';
14
14
  import * as i2$1 from '@angular/common';
15
15
  import * as i6 from '@angular/cdk/stepper';
16
- import { clone, sortBy, isNumber, toPairs, fromPairs, find, cloneDeep, pick } from 'lodash-es';
17
- import * as i3$1 from '@ngx-formly/core';
18
- import * as i4$1 from 'ngx-bootstrap/tooltip';
19
- import { TooltipModule } from 'ngx-bootstrap/tooltip';
20
- import * as i2$2 from '@c8y/ngx-components/map';
21
- import { defaultMapConfig, getC8yMarker, MapComponent, MapModule } from '@c8y/ngx-components/map';
22
- import * as i1$2 from '@angular/router';
23
- import * as i4$2 from '@c8y/ngx-components/device-list';
24
- import * as i2$3 from 'ngx-bootstrap/modal';
16
+ import * as i4$1 from '@c8y/ngx-components/device-list';
17
+ import * as i2$2 from 'ngx-bootstrap/modal';
25
18
  import { takeUntil } from 'rxjs/operators';
19
+ import { cloneDeep, clone, sortBy, isNumber, toPairs, fromPairs, find, pick } from 'lodash-es';
26
20
  import * as i7$1 from 'ngx-bootstrap/popover';
27
21
  import { PopoverModule } from 'ngx-bootstrap/popover';
28
22
  import * as i9 from 'ngx-bootstrap/dropdown';
29
23
  import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
24
+ import * as i4$2 from 'ngx-bootstrap/tooltip';
25
+ import { TooltipModule } from 'ngx-bootstrap/tooltip';
26
+ import * as i3$1 from '@ngx-formly/core';
27
+ import * as i2$3 from '@c8y/ngx-components/map';
28
+ import { defaultMapConfig, getC8yMarker, MapComponent, MapModule } from '@c8y/ngx-components/map';
29
+ import * as i1$2 from '@angular/router';
30
30
 
31
31
  const PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED = {
32
32
  EVENT: 'subAssets',
@@ -395,1358 +395,1426 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
395
395
  type: Injectable
396
396
  }], ctorParameters: () => [{ type: i2.InventoryService }] });
397
397
 
398
- class AddGroupComponent {
399
- constructor(fb, addGroupService, alert, subAssetsService, gainsightService, permissionsService) {
400
- this.fb = fb;
401
- this.addGroupService = addGroupService;
402
- this.alert = alert;
403
- this.subAssetsService = subAssetsService;
398
+ class DeleteAssetsModalComponent {
399
+ constructor(translateService, gainsightService) {
400
+ this.translateService = translateService;
404
401
  this.gainsightService = gainsightService;
405
- this.permissionsService = permissionsService;
406
- this.refresh = new EventEmitter();
407
- this.onDeviceQueryStringChange = new EventEmitter();
408
- this.onCancel = new EventEmitter();
409
- this.pendingStatus = false;
410
- this.pagination = { pageSize: 20, currentPage: 1 };
411
- this.selected = [];
412
- this.canCreateGroup = false;
413
- this.canAssignDevice = false;
402
+ this.CURRENT_LOCATION = location.href;
414
403
  this.PRODUCT_EXPERIENCE = PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED;
415
- this.ITEMS_SELECT_LIMIT = 15;
416
- this.btnLabels = {
417
- next: gettext('Next'),
418
- cancel: gettext('Cancel'),
419
- create: gettext('Create')
404
+ this.showWithCascadeCheckbox = true;
405
+ this.showWithDeviceUserCheckbox = false;
406
+ this.closeSubject = new Subject();
407
+ this.labels = { ok: gettext('Delete'), cancel: gettext('Cancel') };
408
+ this.title = gettext('Delete');
409
+ this.status = Status.DANGER;
410
+ this.config = {
411
+ cascade: false,
412
+ withDeviceUser: false
420
413
  };
421
414
  }
422
- onEnterKeyDown(_event) {
423
- // Order matters! Needs to be placed before this.stepper.next
424
- if ((this.isGroupDetailsStep() && !this.canAssignDevice) || this.isAssignDeviceStep()) {
425
- this.createGroup();
426
- return;
415
+ ngOnInit() {
416
+ this.setModalTexts();
417
+ }
418
+ async ngAfterViewInit() {
419
+ try {
420
+ await this.modalRef.result;
421
+ this.onClose();
422
+ }
423
+ catch (error) {
424
+ this.onDismiss();
427
425
  }
428
- this.stepper.next();
429
426
  }
430
- async ngOnInit() {
431
- this.formGroupStepOne = this.fb.group({
432
- name: ['', Validators.required],
433
- description: ['']
434
- });
435
- this.subscription = this.onCancel.subscribe(() => this.resetStepper());
436
- this.canCreateGroup =
437
- this.subAssetsService.canCreateGroup() ||
438
- (await this.permissionsService.canEdit([
439
- Permissions.ROLE_INVENTORY_ADMIN,
440
- Permissions.ROLE_INVENTORY_CREATE,
441
- Permissions.ROLE_MANAGED_OBJECT_ADMIN,
442
- Permissions.ROLE_MANAGED_OBJECT_CREATE
443
- ], {
444
- id: this.currentGroupId
445
- }));
446
- this.canAssignDevice = await this.subAssetsService.canAssignDevice({
447
- id: this.currentGroupId
427
+ onClose() {
428
+ this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.EVENT, {
429
+ component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.DELETE_ASSET.COMPONENTS.DELETE_ASSETS_MODAL,
430
+ result: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.DELETE_ASSET.RESULTS.DELETED,
431
+ url: this.CURRENT_LOCATION
448
432
  });
433
+ this.closeSubject.next(this.config);
434
+ this.closeSubject.complete();
449
435
  }
450
- ngAfterViewInit() {
451
- this.nameInput = this.nameInputRef.nativeElement;
452
- this.setFocusOnNameInput();
453
- }
454
- async createGroup() {
455
- if (this.canCreateGroup === false) {
456
- return;
457
- }
458
- this.pendingStatus = true;
459
- await this.addGroupService.createGroupAndAssignDevices(this.formGroupStepOne.value, this.currentGroupId, this.selected);
460
- this.pendingStatus = false;
461
- this.resetStepper();
462
- const alertMsg = gettext('Group created.');
436
+ onDismiss() {
463
437
  this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.EVENT, {
464
- component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.ADD_GROUP.COMPONENTS.ADD_GROUP,
465
- url: window.location.href,
466
- result: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.ADD_GROUP.RESULTS.ADD_SUCCESS
438
+ component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.DELETE_ASSET.COMPONENTS.DELETE_ASSETS_MODAL,
439
+ result: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.DELETE_ASSET.RESULTS.CANCELED,
440
+ url: this.CURRENT_LOCATION
467
441
  });
468
- this.alert.success(alertMsg);
469
- this.refresh.emit();
470
- this.onCancel.emit();
471
- }
472
- onSelected(selectedDevicesIDs) {
473
- this.selected = selectedDevicesIDs;
474
- }
475
- resetStepper() {
476
- this.stepper.reset();
477
- this.stepper.selectedIndex = 1;
478
- this.selected = [];
479
- }
480
- ngOnDestroy() {
481
- if (this.subscription) {
482
- this.subscription.unsubscribe();
483
- }
484
- }
485
- isGroupDetailsStep() {
486
- return this.stepper.selectedIndex === 0;
487
- }
488
- isAssignDeviceStep() {
489
- return this.stepper.selectedIndex === 1;
442
+ this.closeSubject.complete();
490
443
  }
491
- setFocusOnNameInput() {
492
- if (this.nameInput) {
493
- this.nameInput.focus();
494
- this.nameInput.select();
495
- }
444
+ setModalTexts() {
445
+ this.message = this.translateService.instant(gettext('You are about to delete: "{{name}}". This operation is irreversible. Do you want to proceed?'), { name: this.asset.name });
446
+ this.deleteGroupSubAssetsMsg = this.translateService.instant(gettext('Also delete all devices inside "{{name}}" and its subassets.'), { name: this.asset.name });
496
447
  }
497
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddGroupComponent, deps: [{ token: i1$1.FormBuilder }, { token: AddGroupService }, { token: i3.AlertService }, { token: SubAssetsService }, { token: i3.GainsightService }, { token: i3.Permissions }], target: i0.ɵɵFactoryTarget.Component }); }
498
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AddGroupComponent, selector: "c8y-add-group", inputs: { currentGroupId: "currentGroupId", refresh: "refresh" }, outputs: { onDeviceQueryStringChange: "onDeviceQueryStringChange", onCancel: "onCancel" }, host: { listeners: { "document:keydown.enter": "onEnterKeyDown($event)" } }, viewQueries: [{ propertyName: "stepper", first: true, predicate: C8yStepper, descendants: true }, { propertyName: "nameInputRef", first: true, predicate: ["nameRef"], descendants: true }], ngImport: i0, template: "<div class=\"d-contents\" *ngIf=\"!currentGroupId; else stepper\">\n <ng-container [ngTemplateOutlet]=\"stepper\"></ng-container>\n</div>\n\n<ng-template #stepper>\n <c8y-stepper\n class=\"d-col flex-nowrap no-align-items fit-h c8y-stepper--no-btns\"\n [disableDefaultIcons]=\"{ edit: true, done: false }\"\n [customClasses]=\"['col-md-6', 'col-md-offset-3', 'm-t-24', 'm-b-40', 'p-0', 'flex-no-shrink']\"\n linear\n c8yProductExperience\n inherit\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ADD_GROUP.COMPONENTS.ADD_GROUP }\"\n >\n <cdk-step [stepControl]=\"formGroupStepOne\" [label]=\"'New group' | translate\">\n <div class=\"p-16 p-t-0 flex-no-shrink separator-bottom col-xs-12\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'New group' | translate }}\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 flex-grow no-gutter\">\n <div class=\"card-inner-scroll fit-h\">\n <div class=\"card-block p-b-0\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <c8y-form-group [novalidation]=\"true\">\n <div [formGroup]=\"formGroupStepOne\">\n <c8y-form-group>\n <label translate>Name</label>\n <input\n class=\"form-control\"\n type=\"text\"\n formControlName=\"name\"\n placeholder=\"{{ 'e.g. First floor' | translate }} \"\n maxlength=\"254\"\n #nameRef\n required\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate>Description</label>\n <input\n class=\"form-control\"\n type=\"text\"\n formControlName=\"description\"\n placeholder=\"{{ 'e.g. first floor devices' | translate }}\"\n />\n </c8y-form-group>\n </div>\n </c8y-form-group>\n <c8y-form-group>\n <div [formGroup]=\"formGroupStepOne\"></div>\n </c8y-form-group>\n <div class=\"alert alert-info max-width-100\" translate *ngIf=\"!canAssignDevice\">\n You don't have permission to assign devices.\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <c8y-stepper-buttons\n class=\"d-block card-footer p-24 separator\"\n (onCancel)=\"onCancel.emit()\"\n (onCustom)=\"createGroup()\"\n [disabled]=\"!canCreateGroup\"\n [labels]=\"\n canAssignDevice\n ? { next: btnLabels.next, cancel: btnLabels.cancel }\n : { custom: btnLabels.create, cancel: btnLabels.cancel }\n \"\n [showButtons]=\"\n canAssignDevice ? { next: true, cancel: true } : { custom: true, cancel: true }\n \"\n ></c8y-stepper-buttons>\n </cdk-step>\n <cdk-step [label]=\"'Assign devices' | translate\">\n <div class=\"p-16 p-t-0 flex-no-shrink separator-bottom col-xs-12\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'Assign devices' | translate }}\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 no-gutter flex-grow\">\n <c8y-device-grid\n [title]=\"'Select target devices' | translate\"\n [actionControls]=\"[]\"\n [infiniteScroll]=\"'auto'\"\n [selectable]=\"true\"\n [pagination]=\"pagination\"\n (itemsSelect)=\"onSelected($event)\"\n [refresh]=\"refresh\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ADD_GROUP.COMPONENTS.ADD_GROUP }\"\n ></c8y-device-grid>\n </div>\n <c8y-stepper-buttons\n class=\"d-block card-footer p-24 separator\"\n (onCancel)=\"onCancel.emit()\"\n (onCustom)=\"createGroup()\"\n [labels]=\"{ custom: btnLabels.create }\"\n [disabled]=\"!canAssignDevice\"\n [pending]=\"pendingStatus\"\n ></c8y-stepper-buttons>\n </cdk-step>\n </c8y-stepper>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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: "component", type: i3.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i3.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: i3.C8yStepper, selector: "c8y-stepper", inputs: ["disableDefaultIcons", "disableProgressButtons", "customClasses", "hideStepProgress", "useStepLabelsAsTitlesOnly"], outputs: ["onStepChange"] }, { kind: "component", type: i6.CdkStep, selector: "cdk-step", inputs: ["stepControl", "label", "errorMessage", "aria-label", "aria-labelledby", "state", "editable", "optional", "completed", "hasError"], outputs: ["interacted"], exportAs: ["cdkStep"] }, { kind: "component", type: i3.C8yStepperButtons, selector: "c8y-stepper-buttons", inputs: ["labels", "pending", "disabled", "showButtons"], outputs: ["onCancel", "onNext", "onBack", "onCustom"] }, { kind: "directive", type: i3.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i7.DeviceGridComponent, selector: "c8y-device-grid", inputs: ["dataCallback", "refresh", "title", "loadMoreItemsLabel", "loadingItemsLabel", "legacyConfigKey", "legacyFilterKey", "columns", "pagination", "infiniteScroll", "actionControls", "selectable", "singleSelection", "baseQuery", "bulkActionControls", "headerActionControls", "childDeviceGrid", "parentDeviceId", "withChildren", "showSearch", "activeClassName"], outputs: ["onColumnsChange", "onFilterChange", "onDeviceQueryStringChange", "itemsSelect"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
448
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DeleteAssetsModalComponent, deps: [{ token: i1.TranslateService }, { token: i3.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
449
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DeleteAssetsModalComponent, 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: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.ConfirmModalComponent, selector: "c8y-confirm-modal", inputs: ["title", "body", "confirmOptions", "status", "labels"] }, { 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: "component", type: i3.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i3.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
499
450
  }
500
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddGroupComponent, decorators: [{
451
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DeleteAssetsModalComponent, decorators: [{
501
452
  type: Component,
502
- args: [{ selector: 'c8y-add-group', template: "<div class=\"d-contents\" *ngIf=\"!currentGroupId; else stepper\">\n <ng-container [ngTemplateOutlet]=\"stepper\"></ng-container>\n</div>\n\n<ng-template #stepper>\n <c8y-stepper\n class=\"d-col flex-nowrap no-align-items fit-h c8y-stepper--no-btns\"\n [disableDefaultIcons]=\"{ edit: true, done: false }\"\n [customClasses]=\"['col-md-6', 'col-md-offset-3', 'm-t-24', 'm-b-40', 'p-0', 'flex-no-shrink']\"\n linear\n c8yProductExperience\n inherit\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ADD_GROUP.COMPONENTS.ADD_GROUP }\"\n >\n <cdk-step [stepControl]=\"formGroupStepOne\" [label]=\"'New group' | translate\">\n <div class=\"p-16 p-t-0 flex-no-shrink separator-bottom col-xs-12\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'New group' | translate }}\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 flex-grow no-gutter\">\n <div class=\"card-inner-scroll fit-h\">\n <div class=\"card-block p-b-0\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <c8y-form-group [novalidation]=\"true\">\n <div [formGroup]=\"formGroupStepOne\">\n <c8y-form-group>\n <label translate>Name</label>\n <input\n class=\"form-control\"\n type=\"text\"\n formControlName=\"name\"\n placeholder=\"{{ 'e.g. First floor' | translate }} \"\n maxlength=\"254\"\n #nameRef\n required\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate>Description</label>\n <input\n class=\"form-control\"\n type=\"text\"\n formControlName=\"description\"\n placeholder=\"{{ 'e.g. first floor devices' | translate }}\"\n />\n </c8y-form-group>\n </div>\n </c8y-form-group>\n <c8y-form-group>\n <div [formGroup]=\"formGroupStepOne\"></div>\n </c8y-form-group>\n <div class=\"alert alert-info max-width-100\" translate *ngIf=\"!canAssignDevice\">\n You don't have permission to assign devices.\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <c8y-stepper-buttons\n class=\"d-block card-footer p-24 separator\"\n (onCancel)=\"onCancel.emit()\"\n (onCustom)=\"createGroup()\"\n [disabled]=\"!canCreateGroup\"\n [labels]=\"\n canAssignDevice\n ? { next: btnLabels.next, cancel: btnLabels.cancel }\n : { custom: btnLabels.create, cancel: btnLabels.cancel }\n \"\n [showButtons]=\"\n canAssignDevice ? { next: true, cancel: true } : { custom: true, cancel: true }\n \"\n ></c8y-stepper-buttons>\n </cdk-step>\n <cdk-step [label]=\"'Assign devices' | translate\">\n <div class=\"p-16 p-t-0 flex-no-shrink separator-bottom col-xs-12\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'Assign devices' | translate }}\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 no-gutter flex-grow\">\n <c8y-device-grid\n [title]=\"'Select target devices' | translate\"\n [actionControls]=\"[]\"\n [infiniteScroll]=\"'auto'\"\n [selectable]=\"true\"\n [pagination]=\"pagination\"\n (itemsSelect)=\"onSelected($event)\"\n [refresh]=\"refresh\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ADD_GROUP.COMPONENTS.ADD_GROUP }\"\n ></c8y-device-grid>\n </div>\n <c8y-stepper-buttons\n class=\"d-block card-footer p-24 separator\"\n (onCancel)=\"onCancel.emit()\"\n (onCustom)=\"createGroup()\"\n [labels]=\"{ custom: btnLabels.create }\"\n [disabled]=\"!canAssignDevice\"\n [pending]=\"pendingStatus\"\n ></c8y-stepper-buttons>\n </cdk-step>\n </c8y-stepper>\n</ng-template>\n" }]
503
- }], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: AddGroupService }, { type: i3.AlertService }, { type: SubAssetsService }, { type: i3.GainsightService }, { type: i3.Permissions }], propDecorators: { currentGroupId: [{
453
+ args: [{ selector: 'c8y-delete-assets-modal', 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" }]
454
+ }], ctorParameters: () => [{ type: i1.TranslateService }, { type: i3.GainsightService }], propDecorators: { showWithCascadeCheckbox: [{
504
455
  type: Input
505
- }], refresh: [{
456
+ }], showWithDeviceUserCheckbox: [{
506
457
  type: Input
507
- }], onDeviceQueryStringChange: [{
508
- type: Output
509
- }], onCancel: [{
510
- type: Output
511
- }], stepper: [{
512
- type: ViewChild,
513
- args: [C8yStepper, { static: false }]
514
- }], nameInputRef: [{
458
+ }], asset: [{
459
+ type: Input
460
+ }], modalRef: [{
515
461
  type: ViewChild,
516
- args: ['nameRef', { static: false }]
517
- }], onEnterKeyDown: [{
518
- type: HostListener,
519
- args: ['document:keydown.enter', ['$event']]
462
+ args: ['modalRef', { static: false }]
520
463
  }] } });
521
464
 
522
- class AddGroupModule {
523
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddGroupModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
524
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: AddGroupModule, declarations: [AddGroupComponent], imports: [CoreModule, DeviceGridModule, FormsModule, ReactiveFormsModule], exports: [AddGroupComponent] }); }
525
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddGroupModule, providers: [AddGroupService], imports: [CoreModule, DeviceGridModule, FormsModule, ReactiveFormsModule] }); }
526
- }
527
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddGroupModule, decorators: [{
528
- type: NgModule,
529
- args: [{
530
- declarations: [AddGroupComponent],
531
- imports: [CoreModule, DeviceGridModule, FormsModule, ReactiveFormsModule],
532
- exports: [AddGroupComponent],
533
- providers: [AddGroupService]
534
- }]
535
- }] });
536
-
537
- class AssetPropertiesItemComponent {
538
- constructor(alert, c8yJsonSchemaService, filesService) {
539
- this.alert = alert;
540
- this.c8yJsonSchemaService = c8yJsonSchemaService;
541
- this.filesService = filesService;
465
+ class SmartGroupGridConfigurationStrategy extends AbstractConfigurationStrategy {
466
+ constructor(userPreferencesConfigurationStrategy, context, contextProvider) {
467
+ super(context, contextProvider);
468
+ this.userPreferencesConfigurationStrategy = userPreferencesConfigurationStrategy;
469
+ this.context = context;
470
+ this.contextProvider = contextProvider;
542
471
  }
543
- async ngOnChanges(changes) {
544
- if (changes.isEdit) {
545
- this.resolveJsonSchema();
546
- await this.resolveFile();
472
+ getConfig$(context) {
473
+ const group = cloneDeep(this.retrieveContext(context)?.group);
474
+ if (group?.c8y_DeviceColumnsConfig?.columns?.length) {
475
+ group.c8y_DeviceColumnsConfig.columns = group.c8y_DeviceColumnsConfig.columns.map(column => {
476
+ delete column.filter;
477
+ return column;
478
+ });
547
479
  }
480
+ return of(group?.c8y_DeviceColumnsConfig);
548
481
  }
549
- async resolveFile() {
550
- if (this.file) {
551
- try {
552
- if (this.filesService.fileNamesHaveValidExtension(this.file.name, 'image')) {
553
- const imageFile = await this.filesService.getFile(this.file);
554
- this.previewImage = await this.getPreviewIfImage(imageFile);
555
- }
556
- }
557
- catch (ex) {
558
- this.alert.danger(gettext('File could not be loaded.'));
559
- }
560
- }
482
+ saveConfig$(config, _context) {
483
+ return of(config);
561
484
  }
562
- formComplexPropsValue() {
563
- const complexProps = {};
564
- this.complex.forEach(complexObj => {
565
- if (complexObj.file) {
566
- complexProps[complexObj.key] = complexObj.value;
567
- }
568
- else {
569
- complexProps[complexObj.key] = this.value[complexObj.key];
570
- }
571
- });
572
- return complexProps;
485
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SmartGroupGridConfigurationStrategy, deps: [{ token: i3.UserPreferencesConfigurationStrategy }, { token: DATA_GRID_CONFIGURATION_CONTEXT, optional: true }, { token: DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
486
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SmartGroupGridConfigurationStrategy, providedIn: 'root' }); }
487
+ }
488
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SmartGroupGridConfigurationStrategy, decorators: [{
489
+ type: Injectable,
490
+ args: [{ providedIn: 'root' }]
491
+ }], ctorParameters: () => [{ type: i3.UserPreferencesConfigurationStrategy }, { type: undefined, decorators: [{
492
+ type: Inject,
493
+ args: [DATA_GRID_CONFIGURATION_CONTEXT]
494
+ }, {
495
+ type: Optional
496
+ }] }, { type: undefined, decorators: [{
497
+ type: Inject,
498
+ args: [DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER]
499
+ }, {
500
+ type: Optional
501
+ }] }] });
502
+
503
+ class SubAssetsGridConfigurationStrategy extends AbstractConfigurationStrategy {
504
+ constructor(userPreferencesConfigurationStrategy, smartGroupGridConfigurationStrategy, assetNodeService, context, contextProvider) {
505
+ super(context, contextProvider);
506
+ this.userPreferencesConfigurationStrategy = userPreferencesConfigurationStrategy;
507
+ this.smartGroupGridConfigurationStrategy = smartGroupGridConfigurationStrategy;
508
+ this.assetNodeService = assetNodeService;
509
+ this.context = context;
510
+ this.contextProvider = contextProvider;
573
511
  }
574
- getModel() {
575
- if (this.complex && this.complex.length > 0) {
576
- return {
577
- [this.key]: this.formComplexPropsValue()
578
- };
579
- }
580
- return {
581
- [this.key]: clone(this.value)
582
- };
512
+ getConfig$(context) {
513
+ return this.getStrategy(context).getConfig$(context);
583
514
  }
584
- resolveJsonSchema() {
585
- if (this.jsonSchema) {
586
- const fieldConfig = this.c8yJsonSchemaService.toFieldConfig(this.jsonSchema, this.jsonSchema);
587
- if (this.complex && this.complex.length > 0) {
588
- const orderedFieldConfig = sortBy(fieldConfig.fieldGroup[0].fieldGroup, 'order');
589
- fieldConfig.fieldGroup[0].fieldGroup = orderedFieldConfig;
590
- }
591
- this.form = new FormGroup({});
592
- this.fields = [fieldConfig];
593
- this.model = this.getModel();
515
+ saveConfig$(config, context) {
516
+ return this.getStrategy(context).saveConfig$(config, context);
517
+ }
518
+ getStrategy(ctx) {
519
+ const context = this.retrieveContext(ctx);
520
+ return !!context?.group &&
521
+ this.assetNodeService.isDynamicGroup(context?.group) &&
522
+ context?.group?.c8y_DeviceColumnsConfig
523
+ ? this.smartGroupGridConfigurationStrategy
524
+ : this.userPreferencesConfigurationStrategy;
525
+ }
526
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridConfigurationStrategy, deps: [{ token: i3.UserPreferencesConfigurationStrategy }, { token: SmartGroupGridConfigurationStrategy }, { token: i4.AssetNodeService }, { token: DATA_GRID_CONFIGURATION_CONTEXT, optional: true }, { token: DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
527
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridConfigurationStrategy, providedIn: 'root' }); }
528
+ }
529
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridConfigurationStrategy, decorators: [{
530
+ type: Injectable,
531
+ args: [{ providedIn: 'root' }]
532
+ }], ctorParameters: () => [{ type: i3.UserPreferencesConfigurationStrategy }, { type: SmartGroupGridConfigurationStrategy }, { type: i4.AssetNodeService }, { type: undefined, decorators: [{
533
+ type: Inject,
534
+ args: [DATA_GRID_CONFIGURATION_CONTEXT]
535
+ }, {
536
+ type: Optional
537
+ }] }, { type: undefined, decorators: [{
538
+ type: Inject,
539
+ args: [DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER]
540
+ }, {
541
+ type: Optional
542
+ }] }] });
543
+
544
+ class UnassignModalComponent {
545
+ constructor(translateService, gainsightService) {
546
+ this.translateService = translateService;
547
+ this.gainsightService = gainsightService;
548
+ this.CURRENT_LOCATION = location.href;
549
+ this.closeSubject = new Subject();
550
+ this.labels = { ok: gettext('Unassign'), cancel: gettext('Cancel') };
551
+ this.title = gettext('Unassign');
552
+ this.status = Status.WARNING;
553
+ }
554
+ ngOnInit() {
555
+ this.message = this.translateService.instant(gettext('You are about to unassign "{{name}}". Do you want to proceed?'), { name: this.asset.name });
556
+ }
557
+ async ngAfterViewInit() {
558
+ try {
559
+ await this.modalRef.result;
560
+ this.onClose();
561
+ }
562
+ catch (error) {
563
+ this.onDismiss();
594
564
  }
595
565
  }
596
- async getPreviewIfImage(imageFile) {
597
- return this.filesService.toBase64(imageFile);
566
+ onClose() {
567
+ this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.EVENT, {
568
+ component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.UNASSIGN_MODAL.COMPONENTS.UNASSIGN_MODAL,
569
+ result: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.UNASSIGN_MODAL.RESULTS.ASSET_UNASSIGNED,
570
+ url: this.CURRENT_LOCATION
571
+ });
572
+ this.closeSubject.next(true);
573
+ this.closeSubject.complete();
598
574
  }
599
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetPropertiesItemComponent, deps: [{ token: i3.AlertService }, { token: i3.C8yJSONSchema }, { token: i3.FilesService }], target: i0.ɵɵFactoryTarget.Component }); }
600
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AssetPropertiesItemComponent, 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: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i2$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i2$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: i3$1.FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "component", type: AssetPropertiesItemComponent, selector: "c8y-asset-properties-item", inputs: ["key", "value", "label", "type", "file", "complex", "isEdit", "jsonSchema"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i3.DatePipe, name: "c8yDate" }] }); }
575
+ onDismiss() {
576
+ this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.EVENT, {
577
+ component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.UNASSIGN_MODAL.COMPONENTS.UNASSIGN_MODAL,
578
+ action: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.UNASSIGN_MODAL.ACTIONS.CANCEL,
579
+ url: this.CURRENT_LOCATION
580
+ });
581
+ this.closeSubject.complete();
582
+ }
583
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UnassignModalComponent, deps: [{ token: i1.TranslateService }, { token: i3.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
584
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: UnassignModalComponent, 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: i3.ConfirmModalComponent, selector: "c8y-confirm-modal", inputs: ["title", "body", "confirmOptions", "status", "labels"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
601
585
  }
602
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetPropertiesItemComponent, decorators: [{
586
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UnassignModalComponent, decorators: [{
603
587
  type: Component,
604
- args: [{ selector: 'c8y-asset-properties-item', 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" }]
605
- }], ctorParameters: () => [{ type: i3.AlertService }, { type: i3.C8yJSONSchema }, { type: i3.FilesService }], propDecorators: { key: [{
606
- type: Input
607
- }], value: [{
608
- type: Input
609
- }], label: [{
610
- type: Input
611
- }], type: [{
612
- type: Input
613
- }], file: [{
614
- type: Input
615
- }], complex: [{
616
- type: Input
617
- }], isEdit: [{
618
- type: Input
619
- }], jsonSchema: [{
588
+ args: [{ selector: 'c8y-unassign-modal', template: "<c8y-confirm-modal [title]=\"title\" [status]=\"status\" [labels]=\"labels\" #modalRef>\n <span>{{ message | translate }}</span>\n</c8y-confirm-modal>\n" }]
589
+ }], ctorParameters: () => [{ type: i1.TranslateService }, { type: i3.GainsightService }], propDecorators: { asset: [{
620
590
  type: Input
591
+ }], modalRef: [{
592
+ type: ViewChild,
593
+ args: ['modalRef', { static: false }]
621
594
  }] } });
622
595
 
623
- function isFullScreenEnabled(element) {
624
- const doc = element;
625
- return !!(doc.fullscreenElement ||
626
- doc.mozFullScreenElement ||
627
- doc.webkitFullscreenElement ||
628
- doc.msFullscreenElement);
629
- }
630
- function toggleFullscreen(element) {
631
- const elem = element;
632
- const doc = element;
633
- if (!isFullScreenEnabled(element)) {
634
- if (elem.requestFullscreen) {
635
- elem.requestFullscreen();
596
+ class SubAssetsGridComponent {
597
+ get columns() {
598
+ return this._columns;
599
+ }
600
+ set columns(value) {
601
+ this._columns = value ?? this.subAssetsGridService.getDefaultColumns();
602
+ }
603
+ set _pagination(value) {
604
+ if (value) {
605
+ this.pagination = value;
636
606
  }
637
- else if (elem.msRequestFullscreen) {
638
- elem.msRequestFullscreen();
607
+ else {
608
+ this.pagination = this.subAssetsGridService.getDefaultPagination();
639
609
  }
640
- else if (elem.mozRequestFullScreen) {
641
- elem.mozRequestFullScreen();
610
+ }
611
+ set _actionControls(value) {
612
+ if (value) {
613
+ this.actionControls = value;
642
614
  }
643
- else if (elem.webkitRequestFullscreen) {
644
- elem.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
615
+ else {
616
+ this.actionControls = this.subAssetsGridService.getDefaultActionControls();
645
617
  }
646
618
  }
647
- else if (doc.exitFullscreen) {
648
- doc.exitFullscreen();
619
+ set _bulkActionControls(value) {
620
+ if (value) {
621
+ this.bulkActionControls = value;
622
+ }
623
+ else {
624
+ this.bulkActionControls = this.subAssetsGridService.getDefaultBulkActionControls();
625
+ }
649
626
  }
650
- else if (doc.msExitFullscreen) {
651
- doc.msExitFullscreen();
627
+ get isRootGroup() {
628
+ return !this.parentGroup;
652
629
  }
653
- else if (doc.mozCancelFullScreen) {
654
- doc.mozCancelFullScreen();
630
+ get getInfiniteScrollMode() {
631
+ return this.isRootGroup && this.subAssetsGridService.isUsingInventoryRoles()
632
+ ? 'auto'
633
+ : undefined;
655
634
  }
656
- else if (doc.webkitExitFullscreen) {
657
- doc.webkitExitFullscreen();
635
+ set _displayOptions(displayOptions) {
636
+ this.displayOptions = { ...this.displayOptions, ...displayOptions };
658
637
  }
659
- }
660
-
661
- // Düsseldorf
662
- const defaultMapLocation = {
663
- lat: defaultMapConfig.center[0],
664
- lng: defaultMapConfig.center[1]
665
- };
666
- class AssetLocationComponent {
667
- constructor(activatedRouter, mapService) {
668
- this.activatedRouter = activatedRouter;
669
- this.mapService = mapService;
670
- this.config = {
671
- center: defaultMapConfig.center,
672
- zoomLevel: 13,
673
- color: 'green',
674
- icon: 'c8y-icon-location'
638
+ constructor(subAssetsGridService, bsModalService, smartGroupsService, deviceListExtensionService, assetNodeService) {
639
+ this.subAssetsGridService = subAssetsGridService;
640
+ this.bsModalService = bsModalService;
641
+ this.smartGroupsService = smartGroupsService;
642
+ this.deviceListExtensionService = deviceListExtensionService;
643
+ this.assetNodeService = assetNodeService;
644
+ this.PRODUCT_EXPERIENCE = PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED;
645
+ this.title = gettext('Subassets');
646
+ this.emptyStateText = gettext('Add your first group or assign devices using the buttons on the action bar.');
647
+ this.loadingItemsLabel = gettext('Loading assets…');
648
+ this.selectable = false;
649
+ this.baseQuery = {};
650
+ this.filterable = true;
651
+ this.sortable = true;
652
+ this.onColumnsChange = new EventEmitter();
653
+ this.itemsSelect = new EventEmitter();
654
+ this.pagination = this.subAssetsGridService.getDefaultPagination();
655
+ this.showCounterWarning = false;
656
+ this.bulkActionControls = this.subAssetsGridService.getDefaultBulkActionControls();
657
+ this.displayOptions = {
658
+ striped: true,
659
+ bordered: false,
660
+ gridHeader: true,
661
+ filter: true,
662
+ hover: true
675
663
  };
676
- this.isMarkerDraggable = false;
677
- this.isMapClickable = false;
678
- this.showMap = true;
664
+ this.showSearch = false;
665
+ this.noResultsMessage = gettext('No matching items.');
666
+ this.noDataMessage = gettext('No items to display.');
667
+ this.noResultsSubtitle = gettext('Refine your search terms or check your spelling.');
668
+ this.destroyed$ = new Subject();
669
+ this.serverSideDataCallback = this.onDataSourceModifier.bind(this);
679
670
  }
680
- async ngOnInit() {
681
- const leaflet = await this.mapService.getLeaflet();
682
- if (leaflet) {
683
- const { contextData } = !this.activatedRouter.parent || this.activatedRouter.snapshot.data.context
684
- ? this.activatedRouter.snapshot.data
685
- : this.activatedRouter.parent.snapshot.data;
686
- this.assets = contextData ? contextData : this.locationMO;
687
- if (this.assets.c8y_Position.lat && this.assets.c8y_Position.lng)
688
- this.config.center = [this.assets.c8y_Position.lat, this.assets.c8y_Position.lng];
689
- this.setView(this.assets.c8y_Position.lat, this.assets.c8y_Position.lng);
671
+ getGridConfigContext() {
672
+ if (!!this.columnsConfigKey) {
673
+ return { key: this.columnsConfigKey, group: this.parentGroup };
690
674
  }
691
675
  }
692
- ngOnChanges(changes) {
693
- if (changes.isEdit?.currentValue) {
694
- this.showMap = true;
695
- this.isMarkerDraggable = true;
696
- this.isMapClickable = true;
697
- queueMicrotask(() => this.mapView?.map.invalidateSize());
698
- this.mapView?.map.on('click', event => {
699
- this.onClickOfMap(event);
700
- this.updateMarker(event.latlng.lat, event.latlng.lng);
701
- });
702
- this.formSubscription = this.form?.valueChanges.subscribe(value => {
703
- this.updateMarker(value.c8y_Position.lat, value.c8y_Position.lng);
704
- this.setView(value.c8y_Position.lat, value.c8y_Position.lng);
676
+ ngOnInit() {
677
+ const isDynamicGroup = !!this.parentGroup && this.assetNodeService.isDynamicGroup(this.parentGroup);
678
+ if (!this.isRootGroup) {
679
+ (isDynamicGroup
680
+ ? this.deviceListExtensionService.items$
681
+ : of(this.subAssetsGridService.getDefaultColumns(this.filterable, this.sortable)))
682
+ .pipe(takeUntil(this.destroyed$))
683
+ .subscribe(columns => (this.columns = columns));
684
+ }
685
+ if (!this.filterable || !this.sortable) {
686
+ this.displayOptions.filter = this.filterable;
687
+ this.columns.forEach(column => {
688
+ column.filterable = this.filterable;
689
+ column.sortable = this.sortable;
705
690
  });
706
- return;
707
- }
708
- if (!changes.isEdit?.currentValue) {
709
- const isAnyValueMissing = this.checkIfAnyValueIsMissing(this.locationMO?.c8y_Position.lat, this.locationMO?.c8y_Position.lng);
710
- if (isAnyValueMissing) {
711
- this.showMap = false;
712
- return;
713
- }
714
- this.isMarkerDraggable = false;
715
- this.isMapClickable = false;
716
- this.updateMarker(this.locationMO?.c8y_Position.lat, this.locationMO?.c8y_Position.lng);
717
- this.setView(this.locationMO?.c8y_Position.lat, this.locationMO?.c8y_Position.lng);
718
691
  }
692
+ this.setActionControls();
693
+ this.showSearch = isDynamicGroup || !this.parentGroup;
719
694
  }
720
- ngOnDestroy() {
721
- this.formSubscription?.unsubscribe();
722
- if (this.mapView?.markers && this.dragListener) {
723
- this.mapView?.markers.forEach(marker => {
724
- marker.off('drag', this.dragListener);
725
- });
695
+ setActionControls() {
696
+ const actionControls = [];
697
+ const unassignAction = {
698
+ type: 'UNASSIGN',
699
+ icon: 'unlink',
700
+ text: gettext('Unassign'),
701
+ priority: 1000,
702
+ callback: (asset) => this.onUnassignAsset(asset, this.parentGroup),
703
+ showIf: (asset) => this.subAssetsGridService.isDevice(asset) &&
704
+ !this.subAssetsGridService.isSmartGroup(this.parentGroup)
705
+ };
706
+ actionControls.push(unassignAction);
707
+ const deleteAction = {
708
+ type: BuiltInActionType.Delete,
709
+ priority: -Infinity,
710
+ callback: (asset) => this.onDeleteAsset(asset, this.parentGroup),
711
+ showIf: (asset) => {
712
+ if (this.smartGroupsService.isSmartGroup(asset)) {
713
+ return this.subAssetsGridService.canDeleteSmartGroup();
714
+ }
715
+ return true;
716
+ }
717
+ };
718
+ actionControls.push(deleteAction);
719
+ if (!this.actionControls) {
720
+ this.actionControls = actionControls;
726
721
  }
727
722
  }
728
- /**
729
- * This command is used to prefill the latitude and longitude values in the form when the marker is dragged.
730
- */
731
- onMarkerDrag(event) {
732
- if (this.form) {
733
- const properties = this.form.get('c8y_Position');
734
- properties?.get('lat').patchValue(event.target._latlng.lat);
735
- properties?.get('lng').patchValue(event.target._latlng.lng);
736
- }
723
+ onUnassignAsset(asset, parentRef) {
724
+ const initialState = {
725
+ asset
726
+ };
727
+ const modalRef = this.bsModalService.show(UnassignModalComponent, { initialState });
728
+ modalRef.content.closeSubject.subscribe(async (result) => {
729
+ if (result) {
730
+ await this.subAssetsGridService.unassignAsset(asset, parentRef);
731
+ this.refresh.emit();
732
+ }
733
+ });
737
734
  }
738
- /**
739
- * This method is used to update the marker with the specified values and if any one of the values is not availble, sets
740
- * showWarning to true.
741
- * @param latitude - The latitude of the marker
742
- * @param longitude - The longitude of the marker
743
- */
744
- updateMarker(latitude, longitude) {
745
- const isAnyValueMissing = this.checkIfAnyValueIsMissing(latitude, longitude);
746
- if (!isAnyValueMissing) {
747
- [latitude, longitude] = this.setLatLngValues(latitude, longitude);
748
- const asset = {
749
- c8y_Position: {
750
- latitude,
751
- longitude
752
- }
753
- };
754
- if (this.mapView) {
755
- const icon = this.mapView.getAssetIcon(this.assets);
756
- const leafletMarker = this.mapView.leaflet.marker([latitude, longitude], {
757
- icon: icon,
758
- draggable: this.isMarkerDraggable
759
- });
760
- if (this.isMarkerDraggable) {
761
- this.dragListener = event => {
762
- this.onMarkerDrag(event);
763
- };
764
- leafletMarker.on('dragend', this.dragListener);
735
+ async onDeleteAsset(asset, parentRef) {
736
+ const initialState = {
737
+ showWithDeviceUserCheckbox: this.subAssetsGridService.shouldShowWithDeviceUserCheckbox(asset),
738
+ asset,
739
+ showWithCascadeCheckbox: !this.smartGroupsService.isSmartGroup(asset)
740
+ };
741
+ const modalRef = this.bsModalService.show(DeleteAssetsModalComponent, { initialState });
742
+ modalRef.content.closeSubject.subscribe(async (result) => {
743
+ if (result) {
744
+ await this.subAssetsGridService.deleteAsset(asset, parentRef, result);
745
+ if (result.cascade) {
746
+ this.showCounterWarning = true;
765
747
  }
766
- this.mapView.clearMarkers();
767
- const marker = getC8yMarker(leafletMarker, asset);
768
- this.mapView.addMarkerToMap(marker);
769
- this.setView(latitude, longitude);
748
+ this.refresh.emit();
770
749
  }
771
- return;
772
- }
773
- this.mapView.clearMarkers();
750
+ });
774
751
  }
775
- /**
776
- * This command is used to prefill the latitude and longitude values in the form on click of map.
777
- */
778
- onClickOfMap(event) {
779
- if (this.form) {
780
- const properties = this.form.get('c8y_Position');
781
- properties?.get('lat').patchValue(event.latlng.lat);
782
- properties?.get('lng').patchValue(event.latlng.lng);
783
- this.form.markAsDirty();
752
+ ngOnChanges(changes) {
753
+ if (changes.parentGroup && !changes.parentGroup.firstChange) {
754
+ this.dataGrid.reload();
784
755
  }
785
756
  }
786
- /**
787
- * Used to enable full screen of the map.
788
- */
789
- enableFullscreen() {
790
- toggleFullscreen(this.mapView.mapElement.nativeElement);
757
+ trackByName(_index, column) {
758
+ return column.name;
791
759
  }
792
- /**
793
- * Checks if any one of the values i.e., latitude/longitude is undefined or null.
794
- * @param latitude Latitude value of the position
795
- * @param longitude Longitude value of the position
796
- * @returns returns true if any one of the values are both the values are missing else it returns false.
797
- */
798
- checkIfAnyValueIsMissing(latitude, longitude) {
799
- return this.isNullOrUndefined(latitude) || this.isNullOrUndefined(longitude);
760
+ onReload() {
761
+ this.assetNodeService.rootNode.refresh();
800
762
  }
801
- /**
802
- * Sets the view of the map based on the position of marker.
803
- * @param latitude - Latitude of the marker
804
- * @param longitude Longitude of the marker
805
- */
806
- setView(latitude, longitude) {
807
- if (isNumber(latitude) && isNumber(longitude) && this.mapView) {
808
- [latitude, longitude] = this.setLatLngValues(latitude, longitude);
809
- this.config.center = [latitude, longitude];
810
- this.mapView.center();
763
+ async onDataSourceModifier(dataSourceModifier) {
764
+ const promises = [];
765
+ let counters;
766
+ promises.push(this.subAssetsGridService.getData(dataSourceModifier.columns, dataSourceModifier.pagination, this.parentGroup, this.baseQuery, dataSourceModifier.searchText));
767
+ promises.push(this.subAssetsGridService.getTotal(this.parentGroup, this.baseQuery));
768
+ promises.push(this.subAssetsGridService.getCount(dataSourceModifier.columns, dataSourceModifier.pagination, this.parentGroup, this.baseQuery, dataSourceModifier.searchText));
769
+ const [dataResponse, size, filteredSize] = await Promise.all(promises);
770
+ if (!counters) {
771
+ counters = {
772
+ size,
773
+ filteredSize
774
+ };
811
775
  }
776
+ this.onColumnsChange.emit(dataSourceModifier.columns);
777
+ return {
778
+ res: dataResponse.res,
779
+ data: dataResponse.data,
780
+ paging: dataResponse.paging,
781
+ ...counters
782
+ };
812
783
  }
813
- setLatLngValues(latitude, longitude) {
814
- latitude = this.isNullOrUndefined(latitude) ? defaultMapLocation.lat : latitude;
815
- longitude = this.isNullOrUndefined(longitude) ? defaultMapLocation.lng : longitude;
816
- return [latitude, longitude];
817
- }
818
- isNullOrUndefined(value) {
819
- return value === null || value === undefined;
784
+ ngOnDestroy() {
785
+ this.destroyed$.next();
786
+ this.destroyed$.complete();
820
787
  }
821
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetLocationComponent, deps: [{ token: i1$2.ActivatedRoute }, { token: i2$2.MapService }], target: i0.ɵɵFactoryTarget.Component }); }
822
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AssetLocationComponent, selector: "c8y-asset-location", inputs: { isEdit: "isEdit", locationMO: "locationMO", form: "form" }, viewQueries: [{ propertyName: "mapView", first: true, predicate: MapComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div [hidden]=\"!showMap\">\n <div class=\"row\">\n <button\n class=\"btn btn-link pull-right\"\n style=\"margin-right: 12px\"\n title=\"Full screen\"\n type=\"button\"\n data-cy=\"asset-location-full-screen\"\n (click)=\"enableFullscreen()\"\n >\n <i c8yIcon=\"expand\"></i>\n </button>\n </div>\n <div style=\"width: 100%; height: 400px\">\n <c8y-map\n #map\n [assets]=\"assets\"\n [config]=\"config\"\n ></c8y-map>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: i2$2.MapComponent, selector: "c8y-map", inputs: ["config", "assets", "polyline$", "polylineOptions"], outputs: ["onRealtimeUpdate", "onMove", "onMoveEnd", "onZoomStart", "onZoomEnd", "onMap", "onInit"] }] }); }
788
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridComponent, deps: [{ token: SubAssetsService }, { token: i2$2.BsModalService }, { token: i2.SmartGroupsService }, { token: i4$1.DeviceListExtensionService }, { token: i4.AssetNodeService }], target: i0.ɵɵFactoryTarget.Component }); }
789
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SubAssetsGridComponent, selector: "c8y-sub-assets-grid", inputs: { parentGroup: ["parent-group", "parentGroup"], refresh: "refresh", title: "title", emptyStateText: "emptyStateText", loadingItemsLabel: "loadingItemsLabel", columnsConfigKey: "columnsConfigKey", columns: "columns", _pagination: ["pagination", "_pagination"], _actionControls: ["actionControls", "_actionControls"], selectable: "selectable", baseQuery: "baseQuery", _bulkActionControls: ["bulkActionControls", "_bulkActionControls"], filterable: "filterable", sortable: "sortable", _displayOptions: ["displayOptions", "_displayOptions"] }, outputs: { onColumnsChange: "onColumnsChange", itemsSelect: "itemsSelect" }, providers: [
790
+ {
791
+ provide: UserPreferencesConfigurationStrategy,
792
+ useClass: UserPreferencesConfigurationStrategy
793
+ },
794
+ {
795
+ provide: SmartGroupGridConfigurationStrategy,
796
+ useClass: SmartGroupGridConfigurationStrategy
797
+ },
798
+ {
799
+ provide: DATA_GRID_CONFIGURATION_STRATEGY,
800
+ useClass: SubAssetsGridConfigurationStrategy
801
+ },
802
+ {
803
+ provide: DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER,
804
+ useExisting: SubAssetsGridComponent
805
+ }
806
+ ], viewQueries: [{ propertyName: "dataGrid", first: true, predicate: DataGridComponent, descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<c8y-data-grid\n [title]=\"title\"\n [loadingItemsLabel]=\"loadingItemsLabel\"\n [columns]=\"columns\"\n [pagination]=\"pagination\"\n [actionControls]=\"actionControls\"\n [selectable]=\"selectable\"\n [bulkActionControls]=\"bulkActionControls\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [infiniteScroll]=\"getInfiniteScrollMode\"\n [showCounterWarning]=\"showCounterWarning\"\n [refresh]=\"refresh\"\n [showSearch]=\"showSearch\"\n [displayOptions]=\"displayOptions\"\n (itemsSelect)=\"itemsSelect.emit($event)\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n (onReload)=\"onReload()\"\n>\n <c8y-ui-empty-state\n [icon]=\"'c8y-group-add'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (emptyStateText | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n\n <ng-container *ngFor=\"let column of columns; trackBy: trackByName\">\n <c8y-column [name]=\"column.name\"></c8y-column>\n </ng-container>\n</c8y-data-grid>\n", dependencies: [{ kind: "component", type: i3.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i3.EmptyStateContextDirective, selector: "[emptyStateContext]" }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.ColumnDirective, selector: "c8y-column", inputs: ["name"] }, { kind: "component", type: i3.DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "columns", "rows", "pagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "directive", type: i3.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
823
807
  }
824
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetLocationComponent, decorators: [{
808
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridComponent, decorators: [{
825
809
  type: Component,
826
- args: [{ selector: 'c8y-asset-location', template: "<div [hidden]=\"!showMap\">\n <div class=\"row\">\n <button\n class=\"btn btn-link pull-right\"\n style=\"margin-right: 12px\"\n title=\"Full screen\"\n type=\"button\"\n data-cy=\"asset-location-full-screen\"\n (click)=\"enableFullscreen()\"\n >\n <i c8yIcon=\"expand\"></i>\n </button>\n </div>\n <div style=\"width: 100%; height: 400px\">\n <c8y-map\n #map\n [assets]=\"assets\"\n [config]=\"config\"\n ></c8y-map>\n </div>\n</div>\n" }]
827
- }], ctorParameters: () => [{ type: i1$2.ActivatedRoute }, { type: i2$2.MapService }], propDecorators: { mapView: [{
828
- type: ViewChild,
829
- args: [MapComponent]
830
- }], isEdit: [{
810
+ args: [{ selector: 'c8y-sub-assets-grid', providers: [
811
+ {
812
+ provide: UserPreferencesConfigurationStrategy,
813
+ useClass: UserPreferencesConfigurationStrategy
814
+ },
815
+ {
816
+ provide: SmartGroupGridConfigurationStrategy,
817
+ useClass: SmartGroupGridConfigurationStrategy
818
+ },
819
+ {
820
+ provide: DATA_GRID_CONFIGURATION_STRATEGY,
821
+ useClass: SubAssetsGridConfigurationStrategy
822
+ },
823
+ {
824
+ provide: DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER,
825
+ useExisting: SubAssetsGridComponent
826
+ }
827
+ ], template: "<c8y-data-grid\n [title]=\"title\"\n [loadingItemsLabel]=\"loadingItemsLabel\"\n [columns]=\"columns\"\n [pagination]=\"pagination\"\n [actionControls]=\"actionControls\"\n [selectable]=\"selectable\"\n [bulkActionControls]=\"bulkActionControls\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [infiniteScroll]=\"getInfiniteScrollMode\"\n [showCounterWarning]=\"showCounterWarning\"\n [refresh]=\"refresh\"\n [showSearch]=\"showSearch\"\n [displayOptions]=\"displayOptions\"\n (itemsSelect)=\"itemsSelect.emit($event)\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n (onReload)=\"onReload()\"\n>\n <c8y-ui-empty-state\n [icon]=\"'c8y-group-add'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (emptyStateText | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n\n <ng-container *ngFor=\"let column of columns; trackBy: trackByName\">\n <c8y-column [name]=\"column.name\"></c8y-column>\n </ng-container>\n</c8y-data-grid>\n" }]
828
+ }], ctorParameters: () => [{ type: SubAssetsService }, { type: i2$2.BsModalService }, { type: i2.SmartGroupsService }, { type: i4$1.DeviceListExtensionService }, { type: i4.AssetNodeService }], propDecorators: { parentGroup: [{
829
+ type: Input,
830
+ args: ['parent-group']
831
+ }], refresh: [{
831
832
  type: Input
832
- }], locationMO: [{
833
+ }], title: [{
833
834
  type: Input
834
- }], form: [{
835
+ }], emptyStateText: [{
835
836
  type: Input
836
- }] } });
837
-
838
- class AssetPropertiesComponent {
839
- constructor(assetTypes, inventory, inventoryBinary, alert) {
840
- this.assetTypes = assetTypes;
841
- this.inventory = inventory;
842
- this.inventoryBinary = inventoryBinary;
843
- this.alert = alert;
844
- this.assetChange = new EventEmitter();
845
- this.properties = [];
846
- this.customProperties = [];
847
- this.isEdit = false;
848
- this.isLoading = false;
849
- }
850
- ngOnChanges(changes) {
851
- if (changes.asset) {
852
- // Back button handling, as component is not destroyed
853
- this.assetType = undefined;
854
- this.customProperties = [];
855
- this.loadAsset();
856
- }
857
- }
858
- async loadAsset() {
859
- this.isLoading = true;
860
- const assetType$ = this.assetTypes.getAssetTypeByName$(this.asset.type);
861
- this.assetType = await firstValueFrom(assetType$);
862
- try {
863
- this.properties = this.keepOrder(this.assetType?.c8y_IsAssetType?.properties, this.properties);
864
- }
865
- catch (ex) {
866
- console.warn(ex);
867
- }
868
- this.customProperties = await this.resolveCustomProperties(this.properties);
869
- this.isLoading = false;
837
+ }], loadingItemsLabel: [{
838
+ type: Input
839
+ }], columnsConfigKey: [{
840
+ type: Input
841
+ }], columns: [{
842
+ type: Input
843
+ }], _pagination: [{
844
+ type: Input,
845
+ args: ['pagination']
846
+ }], _actionControls: [{
847
+ type: Input,
848
+ args: ['actionControls']
849
+ }], selectable: [{
850
+ type: Input
851
+ }], baseQuery: [{
852
+ type: Input
853
+ }], _bulkActionControls: [{
854
+ type: Input,
855
+ args: ['bulkActionControls']
856
+ }], filterable: [{
857
+ type: Input
858
+ }], sortable: [{
859
+ type: Input
860
+ }], onColumnsChange: [{
861
+ type: Output
862
+ }], itemsSelect: [{
863
+ type: Output
864
+ }], dataGrid: [{
865
+ type: ViewChild,
866
+ args: [DataGridComponent, { static: true }]
867
+ }], _displayOptions: [{
868
+ type: Input,
869
+ args: ['displayOptions']
870
+ }] } });
871
+
872
+ class AssignChildDevicesComponent {
873
+ constructor(alert, subAssetsService, inventoryService) {
874
+ this.alert = alert;
875
+ this.subAssetsService = subAssetsService;
876
+ this.inventoryService = inventoryService;
877
+ this.onCancel = new EventEmitter();
878
+ this.onSelectedDevices = new EventEmitter();
879
+ this.refresh = new EventEmitter();
880
+ this.onlySelect = false; // if true, devices are only selected, not assigned
881
+ this.selected = [];
882
+ this.canAssignDevice = false;
883
+ this.pendingStatus = false;
870
884
  }
871
- async resolveCustomProperties(managedObjects) {
872
- const properties = [];
873
- for (const mo of managedObjects) {
874
- if (mo.c8y_JsonSchema) {
875
- const [item] = await this.parseItem(mo, mo.c8y_JsonSchema.properties, this.asset);
876
- this.setItemRequired(item, mo);
877
- properties.push(item);
878
- }
885
+ onEnterKeyDown(_event) {
886
+ if (this.selected.length > 0) {
887
+ this.assignDevices();
879
888
  }
880
- return properties;
881
889
  }
882
- deleteTitleFromMOJsonSchema(mo) {
883
- const schemaProperties = mo?.c8y_JsonSchema?.properties;
884
- const property = Object.keys(schemaProperties || {})[0];
885
- delete (mo?.c8y_JsonSchema?.properties[property] || {}).title;
890
+ onEscapeKeyDown(_event) {
891
+ this.onCancel.emit();
886
892
  }
887
- /**
888
- * This method is used to order the complex properties in the order specified by the user in asset properties screen.
889
- * @param mo - Managed object of the complex property associated with the asset.
890
- */
891
- orderComplexProperties(mo) {
892
- const complexProperties = mo.c8y_JsonSchema.properties[mo.name]?.['properties'];
893
- const keyValuesArray = toPairs(complexProperties);
894
- const orderedProperties = sortBy(keyValuesArray, ([, value]) => value.order);
895
- mo.c8y_JsonSchema.properties[mo.name]['properties'] = fromPairs(orderedProperties);
893
+ async ngOnInit() {
894
+ this.setNotIncludedInGroupQuery();
895
+ this.canAssignDevice = await this.subAssetsService.canAssignDevice({
896
+ id: this.currentGroupId
897
+ });
896
898
  }
897
- async parseItem(mo, properties, asset) {
898
- if (!asset) {
899
- return [];
899
+ setNotIncludedInGroupQuery() {
900
+ const notIncludedInGroupQuery = { __not: { __bygroupid: this.currentGroupId } };
901
+ this.baseQuery = notIncludedInGroupQuery;
902
+ }
903
+ async assignDevices() {
904
+ if (this.canAssignDevice === false) {
905
+ return;
900
906
  }
901
- const keys = Object.keys(properties);
902
- const items = [];
903
- for (const key of keys) {
904
- const type = properties[key].type;
905
- const title = properties[key].title;
906
- let value = this.getTypeValue(type, asset[key]);
907
- let file;
908
- if (type === 'file' && value) {
909
- const fileId = typeof value === 'object' ? value[0]?.file?.id : value;
910
- const fileData = await this.getFileManagedObject(fileId);
911
- file = fileData;
912
- value = [fileData];
913
- }
914
- else if (type === 'date') {
915
- const valueDate = new Date(value);
916
- value = !isNaN(valueDate.getTime()) ? valueDate : '';
917
- }
918
- if (type === 'object') {
919
- // remove title to avoid excessive property name on asset complex properties form
920
- this.deleteTitleFromMOJsonSchema(mo);
921
- this.orderComplexProperties(mo);
922
- if (!value) {
923
- value = {};
924
- for (const prop in properties[key].properties) {
925
- value[prop] = this.getTypeValue(properties[key].properties[prop].type, null);
926
- }
927
- }
928
- }
929
- items.push({
930
- key,
931
- value,
932
- label: title || mo.label,
933
- type,
934
- description: mo.description,
935
- file,
936
- complex: type === 'object'
937
- ? await this.parseItem(mo, properties[key].properties, value)
938
- : undefined,
939
- isEdit: false,
940
- jsonSchema: mo.c8y_JsonSchema
941
- });
907
+ if (this.onlySelect) {
908
+ this.onSelectedDevices.emit(this.selected);
909
+ this.alert.success(gettext('Child devices selected.'));
910
+ this.onCancel.emit();
911
+ return;
942
912
  }
943
- return items;
944
- }
945
- toggleEdit(prop) {
946
- prop.isEdit = !prop.isEdit;
947
- }
948
- async getFileManagedObject(id) {
913
+ this.pendingStatus = true;
949
914
  try {
950
- const { data } = await this.inventory.detail(id);
951
- return data;
915
+ await this.inventoryService.childAssetsBulkAdd(this.selected, this.currentGroupId);
916
+ this.refresh.emit();
917
+ this.alert.success(gettext('Child devices assigned.'));
952
918
  }
953
- catch (ex) {
954
- this.alert.addServerFailure(ex);
919
+ catch (error) {
920
+ this.alert.danger(gettext('Could not assign child devices.'), error);
955
921
  }
922
+ this.pendingStatus = false;
923
+ this.selected = [];
924
+ this.onCancel.emit();
956
925
  }
957
- async save(propertyValue, prop) {
958
- try {
959
- if (prop.type === 'object') {
960
- this.updateUndefinedToPropTypeValue(prop, propertyValue[prop.key]);
961
- }
962
- else {
963
- this.updateUndefinedToPropTypeValue(prop, propertyValue);
964
- }
965
- propertyValue = await this.uploadFiles(propertyValue, prop.value);
966
- // Avoid making a PUT request containing just the id, as response body might be incomplete
967
- const hasValues = Object.values(propertyValue).some(value => value !== undefined);
968
- if (!hasValues) {
969
- this.toggleEdit(prop);
970
- return;
971
- }
972
- const updatedAsset = { id: this.asset.id, ...propertyValue };
973
- const { data } = await this.inventory.update(updatedAsset);
974
- this.toggleEdit(prop);
975
- this.asset = data;
976
- this.assetChange.emit(this.asset);
977
- await this.loadAsset();
978
- this.alert.success(gettext('Asset properties updated.'));
926
+ onSelected(selectedDevicesIDs) {
927
+ this.selected = selectedDevicesIDs;
928
+ }
929
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssignChildDevicesComponent, deps: [{ token: i3.AlertService }, { token: SubAssetsService }, { token: i2.InventoryService }], target: i0.ɵɵFactoryTarget.Component }); }
930
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AssignChildDevicesComponent, selector: "c8y-assign-child-devices", inputs: { currentGroupId: "currentGroupId", parentDevice: "parentDevice", refresh: "refresh", onlySelect: "onlySelect" }, outputs: { onCancel: "onCancel", onSelectedDevices: "onSelectedDevices" }, host: { listeners: { "document:keydown.enter": "onEnterKeyDown($event)", "document:keydown.escape": "onEscapeKeyDown($event)" } }, ngImport: i0, template: "<div class=\"card-block flex-no-shrink separator-bottom col-xs-12 large-padding p-t-24 p-b-24\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <h4 class=\"text-center text-medium\">\n {{ 'Assign child devices' | translate }}\n </h4>\n </div>\n </div>\n</div>\n\n<c8y-sub-assets-grid\n [title]=\"''\"\n [emptyStateText]=\"'All child devices are already assigned' | translate\"\n [refresh]=\"refresh\"\n [actionControls]=\"[]\"\n [columnsConfigKey]=\"'assign-child-devices'\"\n [selectable]=\"true\"\n [parent-group]=\"parentDevice\"\n [baseQuery]=\"baseQuery\"\n (itemsSelect)=\"onSelected($event)\"\n class=\"d-contents\"\n></c8y-sub-assets-grid>\n\n<div class=\"text-center card-footer p-24 separator\">\n <button\n (click)=\"onCancel.emit()\"\n type=\"button\"\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n >\n <span>{{ 'Cancel' | translate }}</span>\n </button>\n <button\n (click)=\"assignDevices()\"\n type=\"button\"\n class=\"btn btn-primary\"\n [ngClass]=\"{ 'btn-pending': pendingStatus }\"\n title=\"{{ 'Assign' | translate }}\"\n [disabled]=\"selected.length === 0 || !canAssignDevice\"\n >\n <span>{{ 'Assign' | translate }}</span>\n </button>\n</div>\n", dependencies: [{ kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: SubAssetsGridComponent, selector: "c8y-sub-assets-grid", inputs: ["parent-group", "refresh", "title", "emptyStateText", "loadingItemsLabel", "columnsConfigKey", "columns", "pagination", "actionControls", "selectable", "baseQuery", "bulkActionControls", "filterable", "sortable", "displayOptions"], outputs: ["onColumnsChange", "itemsSelect"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
931
+ }
932
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssignChildDevicesComponent, decorators: [{
933
+ type: Component,
934
+ args: [{ selector: 'c8y-assign-child-devices', template: "<div class=\"card-block flex-no-shrink separator-bottom col-xs-12 large-padding p-t-24 p-b-24\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <h4 class=\"text-center text-medium\">\n {{ 'Assign child devices' | translate }}\n </h4>\n </div>\n </div>\n</div>\n\n<c8y-sub-assets-grid\n [title]=\"''\"\n [emptyStateText]=\"'All child devices are already assigned' | translate\"\n [refresh]=\"refresh\"\n [actionControls]=\"[]\"\n [columnsConfigKey]=\"'assign-child-devices'\"\n [selectable]=\"true\"\n [parent-group]=\"parentDevice\"\n [baseQuery]=\"baseQuery\"\n (itemsSelect)=\"onSelected($event)\"\n class=\"d-contents\"\n></c8y-sub-assets-grid>\n\n<div class=\"text-center card-footer p-24 separator\">\n <button\n (click)=\"onCancel.emit()\"\n type=\"button\"\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n >\n <span>{{ 'Cancel' | translate }}</span>\n </button>\n <button\n (click)=\"assignDevices()\"\n type=\"button\"\n class=\"btn btn-primary\"\n [ngClass]=\"{ 'btn-pending': pendingStatus }\"\n title=\"{{ 'Assign' | translate }}\"\n [disabled]=\"selected.length === 0 || !canAssignDevice\"\n >\n <span>{{ 'Assign' | translate }}</span>\n </button>\n</div>\n" }]
935
+ }], ctorParameters: () => [{ type: i3.AlertService }, { type: SubAssetsService }, { type: i2.InventoryService }], propDecorators: { currentGroupId: [{
936
+ type: Input
937
+ }], parentDevice: [{
938
+ type: Input
939
+ }], onCancel: [{
940
+ type: Output
941
+ }], onSelectedDevices: [{
942
+ type: Output
943
+ }], refresh: [{
944
+ type: Input
945
+ }], onlySelect: [{
946
+ type: Input
947
+ }], onEnterKeyDown: [{
948
+ type: HostListener,
949
+ args: ['document:keydown.enter', ['$event']]
950
+ }], onEscapeKeyDown: [{
951
+ type: HostListener,
952
+ args: ['document:keydown.escape', ['$event']]
953
+ }] } });
954
+
955
+ class AddGroupComponent {
956
+ constructor(fb, addGroupService, alert, subAssetsService, gainsightService, permissionsService) {
957
+ this.fb = fb;
958
+ this.addGroupService = addGroupService;
959
+ this.alert = alert;
960
+ this.subAssetsService = subAssetsService;
961
+ this.gainsightService = gainsightService;
962
+ this.permissionsService = permissionsService;
963
+ this.refresh = new EventEmitter();
964
+ this.onDeviceQueryStringChange = new EventEmitter();
965
+ this.onCancel = new EventEmitter();
966
+ this.showAssignChildDevices = false;
967
+ this.actionControls = [];
968
+ this.pendingStatus = false;
969
+ this.pagination = { pageSize: 20, currentPage: 1 };
970
+ this.selected = [];
971
+ this.selectedChildDevices = [];
972
+ this.canCreateGroup = false;
973
+ this.canAssignDevice = false;
974
+ this.PRODUCT_EXPERIENCE = PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED;
975
+ this.ITEMS_SELECT_LIMIT = 15;
976
+ this.btnLabels = {
977
+ next: gettext('Next'),
978
+ cancel: gettext('Cancel'),
979
+ create: gettext('Create')
980
+ };
981
+ }
982
+ onEnterKeyDown(_event) {
983
+ // Order matters! Needs to be placed before this.stepper.next
984
+ if ((this.isGroupDetailsStep() && !this.canAssignDevice) || this.isAssignDeviceStep()) {
985
+ this.createGroup();
986
+ return;
979
987
  }
980
- catch (ex) {
981
- this.alert.addServerFailure(ex);
982
- this.toggleEdit(prop);
988
+ this.stepper.next();
989
+ }
990
+ async ngOnInit() {
991
+ this.formGroupStepOne = this.fb.group({
992
+ name: ['', Validators.required],
993
+ description: ['']
994
+ });
995
+ this.subscription = this.onCancel.subscribe(() => this.resetStepper());
996
+ this.canCreateGroup =
997
+ this.subAssetsService.canCreateGroup() ||
998
+ (await this.permissionsService.canEdit([
999
+ Permissions.ROLE_INVENTORY_ADMIN,
1000
+ Permissions.ROLE_INVENTORY_CREATE,
1001
+ Permissions.ROLE_MANAGED_OBJECT_ADMIN,
1002
+ Permissions.ROLE_MANAGED_OBJECT_CREATE
1003
+ ], {
1004
+ id: this.currentGroupId
1005
+ }));
1006
+ this.canAssignDevice = await this.subAssetsService.canAssignDevice({
1007
+ id: this.currentGroupId
1008
+ });
1009
+ this.setActionControls();
1010
+ }
1011
+ setActionControls() {
1012
+ const actionControls = [];
1013
+ const selectChildrenAction = {
1014
+ type: 'SHOW_TARGET_CHILD_DEVICES',
1015
+ icon: 'enter-bottom',
1016
+ text: gettext('Select target child devices'),
1017
+ callback: (asset) => this.selectChildren(asset),
1018
+ showIf: (asset) => asset.childDevices.references.length > 0
1019
+ };
1020
+ actionControls.push(selectChildrenAction);
1021
+ this.actionControls = actionControls;
1022
+ this.refresh.emit();
1023
+ }
1024
+ ngAfterViewInit() {
1025
+ this.nameInput = this.nameInputRef.nativeElement;
1026
+ this.setFocusOnNameInput();
1027
+ }
1028
+ async createGroup() {
1029
+ if (this.canCreateGroup === false) {
1030
+ return;
983
1031
  }
1032
+ this.pendingStatus = true;
1033
+ const combinedDevices = [...this.selected, ...this.selectedChildDevices];
1034
+ await this.addGroupService.createGroupAndAssignDevices(this.formGroupStepOne.value, this.currentGroupId, combinedDevices);
1035
+ this.pendingStatus = false;
1036
+ this.resetStepper();
1037
+ const alertMsg = gettext('Group created.');
1038
+ this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.EVENT, {
1039
+ component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.ADD_GROUP.COMPONENTS.ADD_GROUP,
1040
+ url: window.location.href,
1041
+ result: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.ADD_GROUP.RESULTS.ADD_SUCCESS
1042
+ });
1043
+ this.alert.success(alertMsg);
1044
+ this.refresh.emit();
1045
+ this.onCancel.emit();
984
1046
  }
985
- updateUndefinedToPropTypeValue(prop, propertyValue) {
986
- for (const [key, value] of Object.entries(propertyValue)) {
987
- const property = prop.complex ? find(prop.complex, { key: key }) : prop;
988
- propertyValue[key] = this.getTypeValue(property.type, value);
989
- }
1047
+ onSelected(selectedDevicesIDs) {
1048
+ this.selected = selectedDevicesIDs;
990
1049
  }
991
- getTypeValue(propType, value) {
992
- if (value || (propType === 'boolean' && value !== undefined))
993
- return value;
994
- switch (propType) {
995
- case 'number':
996
- case 'boolean':
997
- return value || value === 0 ? value : null;
998
- default:
999
- return '';
1000
- }
1050
+ onSelectedChildDevices(selectedDevicesIDs) {
1051
+ this.selectedChildDevices = selectedDevicesIDs;
1001
1052
  }
1002
- keepOrder(correctOrderedIds, properties) {
1003
- const orderedProperties = correctOrderedIds.map(({ id }) => {
1004
- const foundProperty = properties.find(property => property.id === id);
1005
- if (!foundProperty) {
1006
- throw new Error('Custom property mismatch');
1007
- }
1008
- return foundProperty;
1009
- });
1010
- return orderedProperties;
1053
+ resetStepper() {
1054
+ this.stepper.reset();
1055
+ this.stepper.selectedIndex = 1;
1056
+ this.selected = [];
1011
1057
  }
1012
- async uploadFiles(model, moId) {
1013
- const keys = Object.keys(model);
1014
- for (const key of keys) {
1015
- if (Array.isArray(model[key]) && model[key][0]?.file instanceof File) {
1016
- try {
1017
- const upload = await this.inventoryBinary.create(model[key][0].file);
1018
- try {
1019
- if (moId && moId[0]) {
1020
- await this.inventory.childAdditionsRemove(moId[0], this.asset.id);
1021
- }
1022
- }
1023
- catch (ex) {
1024
- throw ex;
1025
- }
1026
- model[key] = upload.data.id;
1027
- await this.inventory.childAdditionsAdd(upload.data.id, this.asset.id);
1028
- }
1029
- catch (ex) {
1030
- throw ex;
1031
- }
1032
- }
1058
+ ngOnDestroy() {
1059
+ if (this.subscription) {
1060
+ this.subscription.unsubscribe();
1033
1061
  }
1034
- return model;
1035
1062
  }
1036
- setItemRequired(item, mo) {
1037
- const isAssetPropertyRequired = !!this.assetType?.c8y_IsAssetType?.properties.find(p => p.id === mo.id)?.isRequired;
1038
- if (!isAssetPropertyRequired) {
1039
- return;
1040
- }
1041
- const isComplexProperty = !!item?.complex?.length;
1042
- if (isComplexProperty) {
1043
- const complexProperty = item.jsonSchema?.properties?.[mo.c8y_JsonSchema.key];
1044
- complexProperty.required = item.complex.map(({ key }) => key);
1045
- }
1046
- else {
1047
- item.jsonSchema.required = [mo.c8y_JsonSchema.key];
1063
+ isGroupDetailsStep() {
1064
+ return this.stepper.selectedIndex === 0;
1065
+ }
1066
+ isAssignDeviceStep() {
1067
+ return this.stepper.selectedIndex === 1;
1068
+ }
1069
+ setFocusOnNameInput() {
1070
+ if (this.nameInput) {
1071
+ this.nameInput.focus();
1072
+ this.nameInput.select();
1048
1073
  }
1049
1074
  }
1050
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetPropertiesComponent, deps: [{ token: i3.AssetTypesRealtimeService }, { token: i2.InventoryService }, { token: i2.InventoryBinaryService }, { token: i3.AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
1051
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AssetPropertiesComponent, 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 === 'c8y_Position'\">\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: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: i4$1.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: "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: i3.C8yTranslatePipe, name: "translate" }] }); }
1075
+ selectChildren(asset) {
1076
+ this.showAssignChildDevices = true;
1077
+ this.showChildrenForDevice = asset;
1078
+ }
1079
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddGroupComponent, deps: [{ token: i1$1.FormBuilder }, { token: AddGroupService }, { token: i3.AlertService }, { token: SubAssetsService }, { token: i3.GainsightService }, { token: i3.Permissions }], target: i0.ɵɵFactoryTarget.Component }); }
1080
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AddGroupComponent, selector: "c8y-add-group", inputs: { currentGroupId: "currentGroupId", refresh: "refresh" }, outputs: { onDeviceQueryStringChange: "onDeviceQueryStringChange", onCancel: "onCancel" }, host: { listeners: { "document:keydown.enter": "onEnterKeyDown($event)" } }, viewQueries: [{ propertyName: "stepper", first: true, predicate: C8yStepper, descendants: true }, { propertyName: "nameInputRef", first: true, predicate: ["nameRef"], descendants: true }], ngImport: i0, template: "<div [ngClass]=\"{ drawerOpen: true }\">\n <div class=\"bottom-drawer\">\n <div\n class=\"d-contents\"\n *ngIf=\"!currentGroupId; else stepper\"\n >\n <ng-container [ngTemplateOutlet]=\"stepper\"></ng-container>\n </div>\n </div>\n</div>\n\n<ng-template #stepper>\n <c8y-stepper\n class=\"d-col flex-nowrap no-align-items fit-h c8y-stepper--no-btns\"\n [disableDefaultIcons]=\"{ edit: true, done: false }\"\n [customClasses]=\"['col-md-6', 'col-md-offset-3', 'm-t-24', 'm-b-40', 'p-0', 'flex-no-shrink']\"\n linear\n c8yProductExperience\n inherit\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ADD_GROUP.COMPONENTS.ADD_GROUP }\"\n >\n <cdk-step\n [stepControl]=\"formGroupStepOne\"\n [label]=\"'New group' | translate\"\n >\n <div class=\"p-16 p-t-0 flex-no-shrink separator-bottom col-xs-12\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'New group' | translate }}\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 flex-grow no-gutter\">\n <div class=\"card-inner-scroll fit-h\">\n <div class=\"card-block p-b-0\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <c8y-form-group [novalidation]=\"true\">\n <div [formGroup]=\"formGroupStepOne\">\n <c8y-form-group>\n <label translate>Name</label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. First floor' | translate }} \"\n type=\"text\"\n required\n formControlName=\"name\"\n maxlength=\"254\"\n #nameRef\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate>Description</label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. first floor devices' | translate }}\"\n type=\"text\"\n formControlName=\"description\"\n />\n </c8y-form-group>\n </div>\n </c8y-form-group>\n <c8y-form-group>\n <div [formGroup]=\"formGroupStepOne\"></div>\n </c8y-form-group>\n <div\n class=\"alert alert-info max-width-100\"\n translate\n *ngIf=\"!canAssignDevice\"\n >\n You don't have permission to assign devices.\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <c8y-stepper-buttons\n class=\"d-block card-footer p-24 separator\"\n (onCancel)=\"onCancel.emit()\"\n (onCustom)=\"createGroup()\"\n [disabled]=\"!canCreateGroup\"\n [labels]=\"\n canAssignDevice\n ? { next: btnLabels.next, cancel: btnLabels.cancel }\n : { custom: btnLabels.create, cancel: btnLabels.cancel }\n \"\n [showButtons]=\"\n canAssignDevice ? { next: true, cancel: true } : { custom: true, cancel: true }\n \"\n ></c8y-stepper-buttons>\n </cdk-step>\n <cdk-step [label]=\"'Assign devices' | translate\">\n <div class=\"p-16 p-t-0 flex-no-shrink separator-bottom col-xs-12\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'Assign devices' | translate }}\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 no-gutter flex-grow\">\n <c8y-device-grid\n [title]=\"'Select target devices' | translate\"\n [actionControls]=\"actionControls\"\n [infiniteScroll]=\"'auto'\"\n [selectable]=\"true\"\n [withChildren]=\"true\"\n [pagination]=\"pagination\"\n (itemsSelect)=\"onSelected($event)\"\n [refresh]=\"refresh\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ADD_GROUP.COMPONENTS.ADD_GROUP }\"\n ></c8y-device-grid>\n </div>\n <c8y-stepper-buttons\n class=\"d-block card-footer p-24 separator\"\n (onCancel)=\"onCancel.emit()\"\n (onCustom)=\"createGroup()\"\n [labels]=\"{ custom: btnLabels.create }\"\n [disabled]=\"!canAssignDevice\"\n [pending]=\"pendingStatus\"\n ></c8y-stepper-buttons>\n </cdk-step>\n </c8y-stepper>\n</ng-template>\n\n<div\n [ngClass]=\"{ drawerOpen: showAssignChildDevices }\"\n *ngIf=\"showAssignChildDevices\"\n>\n <div class=\"bottom-drawer m-t-40\">\n <div class=\"d-flex d-col no-align-items fit-h\">\n <c8y-assign-child-devices\n class=\"d-contents\"\n (onCancel)=\"showAssignChildDevices = false\"\n [refresh]=\"refresh\"\n [currentGroupId]=\"currentGroupId\"\n [parentDevice]=\"showChildrenForDevice\"\n [onlySelect]=\"true\"\n (onSelectedDevices)=\"onSelectedChildDevices($event)\"\n ></c8y-assign-child-devices>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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: "component", type: i3.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i3.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: i3.C8yStepper, selector: "c8y-stepper", inputs: ["disableDefaultIcons", "disableProgressButtons", "customClasses", "hideStepProgress", "useStepLabelsAsTitlesOnly"], outputs: ["onStepChange"] }, { kind: "component", type: i6.CdkStep, selector: "cdk-step", inputs: ["stepControl", "label", "errorMessage", "aria-label", "aria-labelledby", "state", "editable", "optional", "completed", "hasError"], outputs: ["interacted"], exportAs: ["cdkStep"] }, { kind: "component", type: i3.C8yStepperButtons, selector: "c8y-stepper-buttons", inputs: ["labels", "pending", "disabled", "showButtons"], outputs: ["onCancel", "onNext", "onBack", "onCustom"] }, { kind: "directive", type: i3.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i7.DeviceGridComponent, selector: "c8y-device-grid", inputs: ["dataCallback", "refresh", "title", "loadMoreItemsLabel", "loadingItemsLabel", "legacyConfigKey", "legacyFilterKey", "columns", "pagination", "infiniteScroll", "actionControls", "selectable", "singleSelection", "baseQuery", "bulkActionControls", "headerActionControls", "childDeviceGrid", "parentDeviceId", "withChildren", "showSearch", "activeClassName"], outputs: ["onColumnsChange", "onFilterChange", "onDeviceQueryStringChange", "itemsSelect"] }, { kind: "component", type: AssignChildDevicesComponent, selector: "c8y-assign-child-devices", inputs: ["currentGroupId", "parentDevice", "refresh", "onlySelect"], outputs: ["onCancel", "onSelectedDevices"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
1052
1081
  }
1053
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetPropertiesComponent, decorators: [{
1082
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddGroupComponent, decorators: [{
1054
1083
  type: Component,
1055
- args: [{ selector: 'c8y-asset-properties', 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 === 'c8y_Position'\">\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" }]
1056
- }], ctorParameters: () => [{ type: i3.AssetTypesRealtimeService }, { type: i2.InventoryService }, { type: i2.InventoryBinaryService }, { type: i3.AlertService }], propDecorators: { asset: [{
1084
+ args: [{ selector: 'c8y-add-group', template: "<div [ngClass]=\"{ drawerOpen: true }\">\n <div class=\"bottom-drawer\">\n <div\n class=\"d-contents\"\n *ngIf=\"!currentGroupId; else stepper\"\n >\n <ng-container [ngTemplateOutlet]=\"stepper\"></ng-container>\n </div>\n </div>\n</div>\n\n<ng-template #stepper>\n <c8y-stepper\n class=\"d-col flex-nowrap no-align-items fit-h c8y-stepper--no-btns\"\n [disableDefaultIcons]=\"{ edit: true, done: false }\"\n [customClasses]=\"['col-md-6', 'col-md-offset-3', 'm-t-24', 'm-b-40', 'p-0', 'flex-no-shrink']\"\n linear\n c8yProductExperience\n inherit\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ADD_GROUP.COMPONENTS.ADD_GROUP }\"\n >\n <cdk-step\n [stepControl]=\"formGroupStepOne\"\n [label]=\"'New group' | translate\"\n >\n <div class=\"p-16 p-t-0 flex-no-shrink separator-bottom col-xs-12\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'New group' | translate }}\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 flex-grow no-gutter\">\n <div class=\"card-inner-scroll fit-h\">\n <div class=\"card-block p-b-0\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <c8y-form-group [novalidation]=\"true\">\n <div [formGroup]=\"formGroupStepOne\">\n <c8y-form-group>\n <label translate>Name</label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. First floor' | translate }} \"\n type=\"text\"\n required\n formControlName=\"name\"\n maxlength=\"254\"\n #nameRef\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate>Description</label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. first floor devices' | translate }}\"\n type=\"text\"\n formControlName=\"description\"\n />\n </c8y-form-group>\n </div>\n </c8y-form-group>\n <c8y-form-group>\n <div [formGroup]=\"formGroupStepOne\"></div>\n </c8y-form-group>\n <div\n class=\"alert alert-info max-width-100\"\n translate\n *ngIf=\"!canAssignDevice\"\n >\n You don't have permission to assign devices.\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <c8y-stepper-buttons\n class=\"d-block card-footer p-24 separator\"\n (onCancel)=\"onCancel.emit()\"\n (onCustom)=\"createGroup()\"\n [disabled]=\"!canCreateGroup\"\n [labels]=\"\n canAssignDevice\n ? { next: btnLabels.next, cancel: btnLabels.cancel }\n : { custom: btnLabels.create, cancel: btnLabels.cancel }\n \"\n [showButtons]=\"\n canAssignDevice ? { next: true, cancel: true } : { custom: true, cancel: true }\n \"\n ></c8y-stepper-buttons>\n </cdk-step>\n <cdk-step [label]=\"'Assign devices' | translate\">\n <div class=\"p-16 p-t-0 flex-no-shrink separator-bottom col-xs-12\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'Assign devices' | translate }}\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 no-gutter flex-grow\">\n <c8y-device-grid\n [title]=\"'Select target devices' | translate\"\n [actionControls]=\"actionControls\"\n [infiniteScroll]=\"'auto'\"\n [selectable]=\"true\"\n [withChildren]=\"true\"\n [pagination]=\"pagination\"\n (itemsSelect)=\"onSelected($event)\"\n [refresh]=\"refresh\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ADD_GROUP.COMPONENTS.ADD_GROUP }\"\n ></c8y-device-grid>\n </div>\n <c8y-stepper-buttons\n class=\"d-block card-footer p-24 separator\"\n (onCancel)=\"onCancel.emit()\"\n (onCustom)=\"createGroup()\"\n [labels]=\"{ custom: btnLabels.create }\"\n [disabled]=\"!canAssignDevice\"\n [pending]=\"pendingStatus\"\n ></c8y-stepper-buttons>\n </cdk-step>\n </c8y-stepper>\n</ng-template>\n\n<div\n [ngClass]=\"{ drawerOpen: showAssignChildDevices }\"\n *ngIf=\"showAssignChildDevices\"\n>\n <div class=\"bottom-drawer m-t-40\">\n <div class=\"d-flex d-col no-align-items fit-h\">\n <c8y-assign-child-devices\n class=\"d-contents\"\n (onCancel)=\"showAssignChildDevices = false\"\n [refresh]=\"refresh\"\n [currentGroupId]=\"currentGroupId\"\n [parentDevice]=\"showChildrenForDevice\"\n [onlySelect]=\"true\"\n (onSelectedDevices)=\"onSelectedChildDevices($event)\"\n ></c8y-assign-child-devices>\n </div>\n </div>\n</div>\n" }]
1085
+ }], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: AddGroupService }, { type: i3.AlertService }, { type: SubAssetsService }, { type: i3.GainsightService }, { type: i3.Permissions }], propDecorators: { currentGroupId: [{
1057
1086
  type: Input
1058
- }], assetChange: [{
1059
- type: Output
1060
- }], properties: [{
1087
+ }], refresh: [{
1061
1088
  type: Input
1089
+ }], onDeviceQueryStringChange: [{
1090
+ type: Output
1091
+ }], onCancel: [{
1092
+ type: Output
1093
+ }], stepper: [{
1094
+ type: ViewChild,
1095
+ args: [C8yStepper, { static: false }]
1096
+ }], nameInputRef: [{
1097
+ type: ViewChild,
1098
+ args: ['nameRef', { static: false }]
1099
+ }], onEnterKeyDown: [{
1100
+ type: HostListener,
1101
+ args: ['document:keydown.enter', ['$event']]
1062
1102
  }] } });
1063
1103
 
1064
- class DeleteAssetsModalComponent {
1065
- constructor(translateService, gainsightService) {
1066
- this.translateService = translateService;
1104
+ class AssignDevicesComponent {
1105
+ static { this.GRID_CONFIG_CONTEXT = {
1106
+ key: 'assign-devices-grid',
1107
+ configFilter: {
1108
+ filter: false
1109
+ }
1110
+ }; }
1111
+ constructor(alert, subAssetsService, inventoryService, gainsightService) {
1112
+ this.alert = alert;
1113
+ this.subAssetsService = subAssetsService;
1114
+ this.inventoryService = inventoryService;
1067
1115
  this.gainsightService = gainsightService;
1068
1116
  this.CURRENT_LOCATION = location.href;
1069
1117
  this.PRODUCT_EXPERIENCE = PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED;
1070
- this.showWithCascadeCheckbox = true;
1071
- this.showWithDeviceUserCheckbox = false;
1072
- this.closeSubject = new Subject();
1073
- this.labels = { ok: gettext('Delete'), cancel: gettext('Cancel') };
1074
- this.title = gettext('Delete');
1075
- this.status = Status.DANGER;
1076
- this.config = {
1077
- cascade: false,
1078
- withDeviceUser: false
1118
+ this.refresh = new EventEmitter();
1119
+ this.onCancel = new EventEmitter();
1120
+ this.onShowChildDevices = new EventEmitter();
1121
+ this.selectedDevice = new EventEmitter();
1122
+ this.pendingStatus = false;
1123
+ this.pagination = { pageSize: 20, currentPage: 1 };
1124
+ this.selected = [];
1125
+ this.canAssignDevice = false;
1126
+ this.actionControls = [];
1127
+ this.headerActionControls = [];
1128
+ this.showChildren = false;
1129
+ this.isSelectable = true;
1130
+ }
1131
+ onEnterKeyDown(_event) {
1132
+ if (this.selected.length > 0) {
1133
+ this.assignDevices();
1134
+ }
1135
+ }
1136
+ async ngOnInit() {
1137
+ this.setNotIncludedInGroupQuery();
1138
+ this.canAssignDevice = await this.subAssetsService.canAssignDevice({
1139
+ id: this.currentGroupId
1140
+ });
1141
+ this.setHeaderActionControls();
1142
+ }
1143
+ setNotIncludedInGroupQuery() {
1144
+ const notIncludedInGroupQuery = { __not: { __bygroupid: this.currentGroupId } };
1145
+ this.baseQuery = notIncludedInGroupQuery;
1146
+ }
1147
+ setHeaderActionControls() {
1148
+ const headerActionControls = [];
1149
+ const showChildDevices = {
1150
+ type: 'DISPLAY_CHILD_DEVICES_BUTTON',
1151
+ text: gettext('Enable child devices selection'),
1152
+ template: this.showDevicesToggle,
1153
+ callback: () => {
1154
+ this.showChildren = !this.showChildren;
1155
+ this.setActionControls(this.showChildren);
1156
+ this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.EVENT, {
1157
+ component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES,
1158
+ action: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.ASSIGN_DEVICES.ACTIONS.DISPLAY_CHILD_DEVICES,
1159
+ url: this.CURRENT_LOCATION
1160
+ });
1161
+ }
1079
1162
  };
1163
+ headerActionControls.push(showChildDevices);
1164
+ this.headerActionControls = headerActionControls;
1080
1165
  }
1081
- ngOnInit() {
1082
- this.setModalTexts();
1166
+ setActionControls(showChildren) {
1167
+ const actionControls = [];
1168
+ const selectChildrenAction = {
1169
+ type: 'SHOW_TARGET_CHILD_DEVICES',
1170
+ icon: 'enter-bottom',
1171
+ text: gettext('Select target child devices'),
1172
+ callback: (asset) => this.selectChildren(asset),
1173
+ showIf: (asset) => asset.childDevices.references.length > 0
1174
+ };
1175
+ if (showChildren) {
1176
+ actionControls.push(selectChildrenAction);
1177
+ }
1178
+ this.actionControls = actionControls;
1179
+ this.refresh.emit();
1083
1180
  }
1084
- async ngAfterViewInit() {
1181
+ async assignDevices() {
1182
+ if (this.canAssignDevice === false) {
1183
+ return;
1184
+ }
1185
+ this.pendingStatus = true;
1085
1186
  try {
1086
- await this.modalRef.result;
1087
- this.onClose();
1187
+ await this.inventoryService.childAssetsBulkAdd(this.selected, this.currentGroupId);
1188
+ this.refresh.emit();
1189
+ this.alert.success(gettext('Devices assigned.'));
1088
1190
  }
1089
1191
  catch (error) {
1090
- this.onDismiss();
1192
+ this.alert.danger(gettext('Could not assign devices.'), error);
1091
1193
  }
1194
+ this.pendingStatus = false;
1195
+ this.selected = [];
1196
+ this.onCancel.emit();
1092
1197
  }
1093
- onClose() {
1094
- this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.EVENT, {
1095
- component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.DELETE_ASSET.COMPONENTS.DELETE_ASSETS_MODAL,
1096
- result: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.DELETE_ASSET.RESULTS.DELETED,
1097
- url: this.CURRENT_LOCATION
1098
- });
1099
- this.closeSubject.next(this.config);
1100
- this.closeSubject.complete();
1101
- }
1102
- onDismiss() {
1103
- this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.EVENT, {
1104
- component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.DELETE_ASSET.COMPONENTS.DELETE_ASSETS_MODAL,
1105
- result: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.DELETE_ASSET.RESULTS.CANCELED,
1106
- url: this.CURRENT_LOCATION
1107
- });
1108
- this.closeSubject.complete();
1198
+ onSelected(selectedDevicesIDs) {
1199
+ this.selected = selectedDevicesIDs;
1109
1200
  }
1110
- setModalTexts() {
1111
- this.message = this.translateService.instant(gettext('You are about to delete: "{{name}}". This operation is irreversible. Do you want to proceed?'), { name: this.asset.name });
1112
- this.deleteGroupSubAssetsMsg = this.translateService.instant(gettext('Also delete all devices inside "{{name}}" and its subassets.'), { name: this.asset.name });
1201
+ selectChildren(asset) {
1202
+ this.onShowChildDevices.emit(true);
1203
+ this.selectedDevice.emit(asset);
1113
1204
  }
1114
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DeleteAssetsModalComponent, deps: [{ token: i1.TranslateService }, { token: i3.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
1115
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DeleteAssetsModalComponent, 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: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.ConfirmModalComponent, selector: "c8y-confirm-modal", inputs: ["title", "body", "confirmOptions", "status", "labels"] }, { 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: "component", type: i3.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i3.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
1205
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssignDevicesComponent, deps: [{ token: i3.AlertService }, { token: SubAssetsService }, { token: i2.InventoryService }, { token: i3.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
1206
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AssignDevicesComponent, selector: "c8y-assign-devices", inputs: { currentGroupId: "currentGroupId", refresh: "refresh" }, outputs: { onCancel: "onCancel", onShowChildDevices: "onShowChildDevices", selectedDevice: "selectedDevice" }, host: { listeners: { "document:keydown.enter": "onEnterKeyDown($event)" } }, providers: [
1207
+ {
1208
+ provide: DATA_GRID_CONFIGURATION_STRATEGY,
1209
+ useClass: UserPreferencesConfigurationStrategy
1210
+ },
1211
+ {
1212
+ provide: DATA_GRID_CONFIGURATION_CONTEXT,
1213
+ useValue: AssignDevicesComponent.GRID_CONFIG_CONTEXT
1214
+ }
1215
+ ], viewQueries: [{ propertyName: "showDevicesToggle", first: true, predicate: ["showDevicesToggle"], descendants: true, read: TemplateRef }], ngImport: i0, template: "<div class=\"card-block flex-no-shrink separator-bottom col-xs-12 large-padding p-t-24 p-b-24\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'Assign devices' | translate }}\n </div>\n </div>\n </div>\n</div>\n<c8y-device-grid\n class=\"flex-grow col-xs-12 no-gutter\"\n [title]=\"''\"\n [actionControls]=\"actionControls\"\n [infiniteScroll]=\"'auto'\"\n [selectable]=\"isSelectable\"\n [pagination]=\"pagination\"\n (itemsSelect)=\"onSelected($event)\"\n [refresh]=\"refresh\"\n [baseQuery]=\"baseQuery\"\n [headerActionControls]=\"headerActionControls\"\n [withChildren]=\"true\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES }\"\n></c8y-device-grid>\n\n<div class=\"text-center card-footer p-24 separator\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"onCancel.emit()\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES,\n action: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.ACTIONS.CANCEL\n }\"\n >\n <span>{{ 'Cancel' | translate }}</span>\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Assign' | translate }}\"\n type=\"button\"\n [ngClass]=\"{ 'btn-pending': pendingStatus }\"\n (click)=\"assignDevices()\"\n [disabled]=\"selected.length === 0 || !canAssignDevice\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES,\n action: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.ACTIONS.ASSIGN\n }\"\n >\n <span>{{ 'Assign' | translate }}</span>\n </button>\n</div>\n\n<ng-template\n #showDevicesToggle\n let-control=\"headerActionControl\"\n>\n <label\n class=\"c8y-switch a-s-center\"\n title=\"{{ control.text | translate }}\"\n >\n <input\n type=\"checkbox\"\n [(ngModel)]=\"showChildren\"\n (click)=\"control.callback()\"\n />\n <span></span>\n <span>{{ control.text | translate }}</span>\n </label>\n <button\n class=\"btn-help m-r-16 a-s-center\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"childDevicesPop\"\n placement=\"bottom\"\n triggers=\"focus\"\n type=\"button\"\n ></button>\n <ng-template #childDevicesPop>\n <span translate>\n Displays the button\n <span\n class=\"btn btn-dot btn-icon no-pointer\"\n title=\"Child devices icon\"\n >\n <i class=\"text-primary dlt-c8y-icon-enter-bottom\"></i>\n </span>\n next to target devices with children. Clicking it displays a list with all child devices of\n the selected target device.\n </span>\n </ng-template>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "component", type: i7.DeviceGridComponent, selector: "c8y-device-grid", inputs: ["dataCallback", "refresh", "title", "loadMoreItemsLabel", "loadingItemsLabel", "legacyConfigKey", "legacyFilterKey", "columns", "pagination", "infiniteScroll", "actionControls", "selectable", "singleSelection", "baseQuery", "bulkActionControls", "headerActionControls", "childDeviceGrid", "parentDeviceId", "withChildren", "showSearch", "activeClassName"], outputs: ["onColumnsChange", "onFilterChange", "onDeviceQueryStringChange", "itemsSelect"] }, { kind: "directive", type: i7$1.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
1116
1216
  }
1117
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DeleteAssetsModalComponent, decorators: [{
1217
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssignDevicesComponent, decorators: [{
1118
1218
  type: Component,
1119
- args: [{ selector: 'c8y-delete-assets-modal', 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" }]
1120
- }], ctorParameters: () => [{ type: i1.TranslateService }, { type: i3.GainsightService }], propDecorators: { showWithCascadeCheckbox: [{
1121
- type: Input
1122
- }], showWithDeviceUserCheckbox: [{
1219
+ args: [{ selector: 'c8y-assign-devices', providers: [
1220
+ {
1221
+ provide: DATA_GRID_CONFIGURATION_STRATEGY,
1222
+ useClass: UserPreferencesConfigurationStrategy
1223
+ },
1224
+ {
1225
+ provide: DATA_GRID_CONFIGURATION_CONTEXT,
1226
+ useValue: AssignDevicesComponent.GRID_CONFIG_CONTEXT
1227
+ }
1228
+ ], template: "<div class=\"card-block flex-no-shrink separator-bottom col-xs-12 large-padding p-t-24 p-b-24\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'Assign devices' | translate }}\n </div>\n </div>\n </div>\n</div>\n<c8y-device-grid\n class=\"flex-grow col-xs-12 no-gutter\"\n [title]=\"''\"\n [actionControls]=\"actionControls\"\n [infiniteScroll]=\"'auto'\"\n [selectable]=\"isSelectable\"\n [pagination]=\"pagination\"\n (itemsSelect)=\"onSelected($event)\"\n [refresh]=\"refresh\"\n [baseQuery]=\"baseQuery\"\n [headerActionControls]=\"headerActionControls\"\n [withChildren]=\"true\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES }\"\n></c8y-device-grid>\n\n<div class=\"text-center card-footer p-24 separator\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"onCancel.emit()\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES,\n action: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.ACTIONS.CANCEL\n }\"\n >\n <span>{{ 'Cancel' | translate }}</span>\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Assign' | translate }}\"\n type=\"button\"\n [ngClass]=\"{ 'btn-pending': pendingStatus }\"\n (click)=\"assignDevices()\"\n [disabled]=\"selected.length === 0 || !canAssignDevice\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES,\n action: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.ACTIONS.ASSIGN\n }\"\n >\n <span>{{ 'Assign' | translate }}</span>\n </button>\n</div>\n\n<ng-template\n #showDevicesToggle\n let-control=\"headerActionControl\"\n>\n <label\n class=\"c8y-switch a-s-center\"\n title=\"{{ control.text | translate }}\"\n >\n <input\n type=\"checkbox\"\n [(ngModel)]=\"showChildren\"\n (click)=\"control.callback()\"\n />\n <span></span>\n <span>{{ control.text | translate }}</span>\n </label>\n <button\n class=\"btn-help m-r-16 a-s-center\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"childDevicesPop\"\n placement=\"bottom\"\n triggers=\"focus\"\n type=\"button\"\n ></button>\n <ng-template #childDevicesPop>\n <span translate>\n Displays the button\n <span\n class=\"btn btn-dot btn-icon no-pointer\"\n title=\"Child devices icon\"\n >\n <i class=\"text-primary dlt-c8y-icon-enter-bottom\"></i>\n </span>\n next to target devices with children. Clicking it displays a list with all child devices of\n the selected target device.\n </span>\n </ng-template>\n</ng-template>\n" }]
1229
+ }], ctorParameters: () => [{ type: i3.AlertService }, { type: SubAssetsService }, { type: i2.InventoryService }, { type: i3.GainsightService }], propDecorators: { currentGroupId: [{
1123
1230
  type: Input
1124
- }], asset: [{
1231
+ }], refresh: [{
1125
1232
  type: Input
1126
- }], modalRef: [{
1233
+ }], onCancel: [{
1234
+ type: Output
1235
+ }], onShowChildDevices: [{
1236
+ type: Output
1237
+ }], selectedDevice: [{
1238
+ type: Output
1239
+ }], showDevicesToggle: [{
1127
1240
  type: ViewChild,
1128
- args: ['modalRef', { static: false }]
1241
+ args: ['showDevicesToggle', { read: TemplateRef }]
1242
+ }], onEnterKeyDown: [{
1243
+ type: HostListener,
1244
+ args: ['document:keydown.enter', ['$event']]
1129
1245
  }] } });
1130
1246
 
1131
- class SmartGroupGridConfigurationStrategy extends AbstractConfigurationStrategy {
1132
- constructor(userPreferencesConfigurationStrategy, context, contextProvider) {
1133
- super(context, contextProvider);
1134
- this.userPreferencesConfigurationStrategy = userPreferencesConfigurationStrategy;
1135
- this.context = context;
1136
- this.contextProvider = contextProvider;
1137
- }
1138
- getConfig$(context) {
1139
- const group = cloneDeep(this.retrieveContext(context)?.group);
1140
- if (group?.c8y_DeviceColumnsConfig?.columns?.length) {
1141
- group.c8y_DeviceColumnsConfig.columns = group.c8y_DeviceColumnsConfig.columns.map(column => {
1142
- delete column.filter;
1143
- return column;
1144
- });
1145
- }
1146
- return of(group?.c8y_DeviceColumnsConfig);
1147
- }
1148
- saveConfig$(config, _context) {
1149
- return of(config);
1150
- }
1151
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SmartGroupGridConfigurationStrategy, deps: [{ token: i3.UserPreferencesConfigurationStrategy }, { token: DATA_GRID_CONFIGURATION_CONTEXT, optional: true }, { token: DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
1152
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SmartGroupGridConfigurationStrategy, providedIn: 'root' }); }
1247
+ class SubAssetsGridsModule {
1248
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1249
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridsModule, declarations: [AssignDevicesComponent, AssignChildDevicesComponent, SubAssetsGridComponent], imports: [CoreModule, DeviceGridModule, PopoverModule, BsDropdownModule, TooltipModule], exports: [SubAssetsGridComponent, AssignDevicesComponent, AssignChildDevicesComponent] }); }
1250
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridsModule, imports: [CoreModule, DeviceGridModule, PopoverModule, BsDropdownModule, TooltipModule] }); }
1153
1251
  }
1154
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SmartGroupGridConfigurationStrategy, decorators: [{
1155
- type: Injectable,
1156
- args: [{ providedIn: 'root' }]
1157
- }], ctorParameters: () => [{ type: i3.UserPreferencesConfigurationStrategy }, { type: undefined, decorators: [{
1158
- type: Inject,
1159
- args: [DATA_GRID_CONFIGURATION_CONTEXT]
1160
- }, {
1161
- type: Optional
1162
- }] }, { type: undefined, decorators: [{
1163
- type: Inject,
1164
- args: [DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER]
1165
- }, {
1166
- type: Optional
1167
- }] }] });
1252
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridsModule, decorators: [{
1253
+ type: NgModule,
1254
+ args: [{
1255
+ declarations: [AssignDevicesComponent, AssignChildDevicesComponent, SubAssetsGridComponent],
1256
+ imports: [CoreModule, DeviceGridModule, PopoverModule, BsDropdownModule, TooltipModule],
1257
+ exports: [SubAssetsGridComponent, AssignDevicesComponent, AssignChildDevicesComponent]
1258
+ }]
1259
+ }] });
1168
1260
 
1169
- class SubAssetsGridConfigurationStrategy extends AbstractConfigurationStrategy {
1170
- constructor(userPreferencesConfigurationStrategy, smartGroupGridConfigurationStrategy, assetNodeService, context, contextProvider) {
1171
- super(context, contextProvider);
1172
- this.userPreferencesConfigurationStrategy = userPreferencesConfigurationStrategy;
1173
- this.smartGroupGridConfigurationStrategy = smartGroupGridConfigurationStrategy;
1174
- this.assetNodeService = assetNodeService;
1175
- this.context = context;
1176
- this.contextProvider = contextProvider;
1177
- }
1178
- getConfig$(context) {
1179
- return this.getStrategy(context).getConfig$(context);
1180
- }
1181
- saveConfig$(config, context) {
1182
- return this.getStrategy(context).saveConfig$(config, context);
1183
- }
1184
- getStrategy(ctx) {
1185
- const context = this.retrieveContext(ctx);
1186
- return !!context?.group &&
1187
- this.assetNodeService.isDynamicGroup(context?.group) &&
1188
- context?.group?.c8y_DeviceColumnsConfig
1189
- ? this.smartGroupGridConfigurationStrategy
1190
- : this.userPreferencesConfigurationStrategy;
1191
- }
1192
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridConfigurationStrategy, deps: [{ token: i3.UserPreferencesConfigurationStrategy }, { token: SmartGroupGridConfigurationStrategy }, { token: i4.AssetNodeService }, { token: DATA_GRID_CONFIGURATION_CONTEXT, optional: true }, { token: DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
1193
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridConfigurationStrategy, providedIn: 'root' }); }
1261
+ class AddGroupModule {
1262
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddGroupModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1263
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: AddGroupModule, declarations: [AddGroupComponent], imports: [CoreModule,
1264
+ DeviceGridModule,
1265
+ FormsModule,
1266
+ ReactiveFormsModule,
1267
+ PopoverModule,
1268
+ SubAssetsGridsModule], exports: [AddGroupComponent] }); }
1269
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddGroupModule, providers: [AddGroupService], imports: [CoreModule,
1270
+ DeviceGridModule,
1271
+ FormsModule,
1272
+ ReactiveFormsModule,
1273
+ PopoverModule,
1274
+ SubAssetsGridsModule] }); }
1194
1275
  }
1195
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridConfigurationStrategy, decorators: [{
1196
- type: Injectable,
1197
- args: [{ providedIn: 'root' }]
1198
- }], ctorParameters: () => [{ type: i3.UserPreferencesConfigurationStrategy }, { type: SmartGroupGridConfigurationStrategy }, { type: i4.AssetNodeService }, { type: undefined, decorators: [{
1199
- type: Inject,
1200
- args: [DATA_GRID_CONFIGURATION_CONTEXT]
1201
- }, {
1202
- type: Optional
1203
- }] }, { type: undefined, decorators: [{
1204
- type: Inject,
1205
- args: [DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER]
1206
- }, {
1207
- type: Optional
1208
- }] }] });
1276
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddGroupModule, decorators: [{
1277
+ type: NgModule,
1278
+ args: [{
1279
+ declarations: [AddGroupComponent],
1280
+ imports: [
1281
+ CoreModule,
1282
+ DeviceGridModule,
1283
+ FormsModule,
1284
+ ReactiveFormsModule,
1285
+ PopoverModule,
1286
+ SubAssetsGridsModule
1287
+ ],
1288
+ exports: [AddGroupComponent],
1289
+ providers: [AddGroupService]
1290
+ }]
1291
+ }] });
1209
1292
 
1210
- class UnassignModalComponent {
1211
- constructor(translateService, gainsightService) {
1212
- this.translateService = translateService;
1213
- this.gainsightService = gainsightService;
1214
- this.CURRENT_LOCATION = location.href;
1215
- this.closeSubject = new Subject();
1216
- this.labels = { ok: gettext('Unassign'), cancel: gettext('Cancel') };
1217
- this.title = gettext('Unassign');
1218
- this.status = Status.WARNING;
1219
- }
1220
- ngOnInit() {
1221
- this.message = this.translateService.instant(gettext('You are about to unassign "{{name}}". Do you want to proceed?'), { name: this.asset.name });
1293
+ class AssetPropertiesItemComponent {
1294
+ constructor(alert, c8yJsonSchemaService, filesService) {
1295
+ this.alert = alert;
1296
+ this.c8yJsonSchemaService = c8yJsonSchemaService;
1297
+ this.filesService = filesService;
1222
1298
  }
1223
- async ngAfterViewInit() {
1224
- try {
1225
- await this.modalRef.result;
1226
- this.onClose();
1299
+ async ngOnChanges(changes) {
1300
+ if (changes.isEdit) {
1301
+ this.resolveJsonSchema();
1302
+ await this.resolveFile();
1227
1303
  }
1228
- catch (error) {
1229
- this.onDismiss();
1304
+ }
1305
+ async resolveFile() {
1306
+ if (this.file) {
1307
+ try {
1308
+ if (this.filesService.fileNamesHaveValidExtension(this.file.name, 'image')) {
1309
+ const imageFile = await this.filesService.getFile(this.file);
1310
+ this.previewImage = await this.getPreviewIfImage(imageFile);
1311
+ }
1312
+ }
1313
+ catch (ex) {
1314
+ this.alert.danger(gettext('File could not be loaded.'));
1315
+ }
1230
1316
  }
1231
1317
  }
1232
- onClose() {
1233
- this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.EVENT, {
1234
- component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.UNASSIGN_MODAL.COMPONENTS.UNASSIGN_MODAL,
1235
- result: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.UNASSIGN_MODAL.RESULTS.ASSET_UNASSIGNED,
1236
- url: this.CURRENT_LOCATION
1318
+ formComplexPropsValue() {
1319
+ const complexProps = {};
1320
+ this.complex.forEach(complexObj => {
1321
+ if (complexObj.file) {
1322
+ complexProps[complexObj.key] = complexObj.value;
1323
+ }
1324
+ else {
1325
+ complexProps[complexObj.key] = this.value[complexObj.key];
1326
+ }
1237
1327
  });
1238
- this.closeSubject.next(true);
1239
- this.closeSubject.complete();
1328
+ return complexProps;
1240
1329
  }
1241
- onDismiss() {
1242
- this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.EVENT, {
1243
- component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.UNASSIGN_MODAL.COMPONENTS.UNASSIGN_MODAL,
1244
- action: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.UNASSIGN_MODAL.ACTIONS.CANCEL,
1245
- url: this.CURRENT_LOCATION
1246
- });
1247
- this.closeSubject.complete();
1330
+ getModel() {
1331
+ if (this.complex && this.complex.length > 0) {
1332
+ return {
1333
+ [this.key]: this.formComplexPropsValue()
1334
+ };
1335
+ }
1336
+ return {
1337
+ [this.key]: clone(this.value)
1338
+ };
1248
1339
  }
1249
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UnassignModalComponent, deps: [{ token: i1.TranslateService }, { token: i3.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
1250
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: UnassignModalComponent, 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: i3.ConfirmModalComponent, selector: "c8y-confirm-modal", inputs: ["title", "body", "confirmOptions", "status", "labels"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
1340
+ resolveJsonSchema() {
1341
+ if (this.jsonSchema) {
1342
+ const fieldConfig = this.c8yJsonSchemaService.toFieldConfig(this.jsonSchema, this.jsonSchema);
1343
+ if (this.complex && this.complex.length > 0) {
1344
+ const orderedFieldConfig = sortBy(fieldConfig.fieldGroup[0].fieldGroup, 'order');
1345
+ fieldConfig.fieldGroup[0].fieldGroup = orderedFieldConfig;
1346
+ }
1347
+ this.form = new FormGroup({});
1348
+ this.fields = [fieldConfig];
1349
+ this.model = this.getModel();
1350
+ }
1351
+ }
1352
+ async getPreviewIfImage(imageFile) {
1353
+ return this.filesService.toBase64(imageFile);
1354
+ }
1355
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetPropertiesItemComponent, deps: [{ token: i3.AlertService }, { token: i3.C8yJSONSchema }, { token: i3.FilesService }], target: i0.ɵɵFactoryTarget.Component }); }
1356
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AssetPropertiesItemComponent, 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: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i2$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i2$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: i3$1.FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "component", type: AssetPropertiesItemComponent, selector: "c8y-asset-properties-item", inputs: ["key", "value", "label", "type", "file", "complex", "isEdit", "jsonSchema"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i3.DatePipe, name: "c8yDate" }] }); }
1251
1357
  }
1252
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UnassignModalComponent, decorators: [{
1358
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetPropertiesItemComponent, decorators: [{
1253
1359
  type: Component,
1254
- args: [{ selector: 'c8y-unassign-modal', template: "<c8y-confirm-modal [title]=\"title\" [status]=\"status\" [labels]=\"labels\" #modalRef>\n <span>{{ message | translate }}</span>\n</c8y-confirm-modal>\n" }]
1255
- }], ctorParameters: () => [{ type: i1.TranslateService }, { type: i3.GainsightService }], propDecorators: { asset: [{
1360
+ args: [{ selector: 'c8y-asset-properties-item', 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" }]
1361
+ }], ctorParameters: () => [{ type: i3.AlertService }, { type: i3.C8yJSONSchema }, { type: i3.FilesService }], propDecorators: { key: [{
1362
+ type: Input
1363
+ }], value: [{
1364
+ type: Input
1365
+ }], label: [{
1366
+ type: Input
1367
+ }], type: [{
1368
+ type: Input
1369
+ }], file: [{
1370
+ type: Input
1371
+ }], complex: [{
1372
+ type: Input
1373
+ }], isEdit: [{
1374
+ type: Input
1375
+ }], jsonSchema: [{
1256
1376
  type: Input
1257
- }], modalRef: [{
1258
- type: ViewChild,
1259
- args: ['modalRef', { static: false }]
1260
1377
  }] } });
1261
1378
 
1262
- class SubAssetsGridComponent {
1263
- get columns() {
1264
- return this._columns;
1265
- }
1266
- set columns(value) {
1267
- this._columns = value ?? this.subAssetsGridService.getDefaultColumns();
1268
- }
1269
- set _pagination(value) {
1270
- if (value) {
1271
- this.pagination = value;
1379
+ function isFullScreenEnabled(element) {
1380
+ const doc = element;
1381
+ return !!(doc.fullscreenElement ||
1382
+ doc.mozFullScreenElement ||
1383
+ doc.webkitFullscreenElement ||
1384
+ doc.msFullscreenElement);
1385
+ }
1386
+ function toggleFullscreen(element) {
1387
+ const elem = element;
1388
+ const doc = element;
1389
+ if (!isFullScreenEnabled(element)) {
1390
+ if (elem.requestFullscreen) {
1391
+ elem.requestFullscreen();
1272
1392
  }
1273
- else {
1274
- this.pagination = this.subAssetsGridService.getDefaultPagination();
1393
+ else if (elem.msRequestFullscreen) {
1394
+ elem.msRequestFullscreen();
1275
1395
  }
1276
- }
1277
- set _actionControls(value) {
1278
- if (value) {
1279
- this.actionControls = value;
1396
+ else if (elem.mozRequestFullScreen) {
1397
+ elem.mozRequestFullScreen();
1280
1398
  }
1281
- else {
1282
- this.actionControls = this.subAssetsGridService.getDefaultActionControls();
1399
+ else if (elem.webkitRequestFullscreen) {
1400
+ elem.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
1283
1401
  }
1284
1402
  }
1285
- set _bulkActionControls(value) {
1286
- if (value) {
1287
- this.bulkActionControls = value;
1288
- }
1289
- else {
1290
- this.bulkActionControls = this.subAssetsGridService.getDefaultBulkActionControls();
1291
- }
1403
+ else if (doc.exitFullscreen) {
1404
+ doc.exitFullscreen();
1292
1405
  }
1293
- get isRootGroup() {
1294
- return !this.parentGroup;
1406
+ else if (doc.msExitFullscreen) {
1407
+ doc.msExitFullscreen();
1295
1408
  }
1296
- get getInfiniteScrollMode() {
1297
- return this.isRootGroup && this.subAssetsGridService.isUsingInventoryRoles()
1298
- ? 'auto'
1299
- : undefined;
1409
+ else if (doc.mozCancelFullScreen) {
1410
+ doc.mozCancelFullScreen();
1300
1411
  }
1301
- set _displayOptions(displayOptions) {
1302
- this.displayOptions = { ...this.displayOptions, ...displayOptions };
1412
+ else if (doc.webkitExitFullscreen) {
1413
+ doc.webkitExitFullscreen();
1303
1414
  }
1304
- constructor(subAssetsGridService, bsModalService, smartGroupsService, deviceListExtensionService, assetNodeService) {
1305
- this.subAssetsGridService = subAssetsGridService;
1306
- this.bsModalService = bsModalService;
1307
- this.smartGroupsService = smartGroupsService;
1308
- this.deviceListExtensionService = deviceListExtensionService;
1309
- this.assetNodeService = assetNodeService;
1310
- this.PRODUCT_EXPERIENCE = PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED;
1311
- this.title = gettext('Subassets');
1312
- this.emptyStateText = gettext('Add your first group or assign devices using the buttons on the action bar.');
1313
- this.loadingItemsLabel = gettext('Loading assets…');
1314
- this.selectable = false;
1315
- this.baseQuery = {};
1316
- this.filterable = true;
1317
- this.sortable = true;
1318
- this.onColumnsChange = new EventEmitter();
1319
- this.itemsSelect = new EventEmitter();
1320
- this.pagination = this.subAssetsGridService.getDefaultPagination();
1321
- this.showCounterWarning = false;
1322
- this.bulkActionControls = this.subAssetsGridService.getDefaultBulkActionControls();
1323
- this.displayOptions = {
1324
- striped: true,
1325
- bordered: false,
1326
- gridHeader: true,
1327
- filter: true,
1328
- hover: true
1415
+ }
1416
+
1417
+ // Düsseldorf
1418
+ const defaultMapLocation = {
1419
+ lat: defaultMapConfig.center[0],
1420
+ lng: defaultMapConfig.center[1]
1421
+ };
1422
+ class AssetLocationComponent {
1423
+ constructor(activatedRouter, mapService) {
1424
+ this.activatedRouter = activatedRouter;
1425
+ this.mapService = mapService;
1426
+ this.config = {
1427
+ center: defaultMapConfig.center,
1428
+ zoomLevel: 13,
1429
+ color: 'green',
1430
+ icon: 'c8y-icon-location'
1329
1431
  };
1330
- this.showSearch = false;
1331
- this.noResultsMessage = gettext('No matching items.');
1332
- this.noDataMessage = gettext('No items to display.');
1333
- this.noResultsSubtitle = gettext('Refine your search terms or check your spelling.');
1334
- this.destroyed$ = new Subject();
1335
- this.serverSideDataCallback = this.onDataSourceModifier.bind(this);
1432
+ this.isMarkerDraggable = false;
1433
+ this.isMapClickable = false;
1434
+ this.showMap = true;
1336
1435
  }
1337
- getGridConfigContext() {
1338
- if (!!this.columnsConfigKey) {
1339
- return { key: this.columnsConfigKey, group: this.parentGroup };
1436
+ async ngOnInit() {
1437
+ const leaflet = await this.mapService.getLeaflet();
1438
+ if (leaflet) {
1439
+ const { contextData } = !this.activatedRouter.parent || this.activatedRouter.snapshot.data.context
1440
+ ? this.activatedRouter.snapshot.data
1441
+ : this.activatedRouter.parent.snapshot.data;
1442
+ this.assets = contextData ? contextData : this.locationMO;
1443
+ if (this.assets.c8y_Position.lat && this.assets.c8y_Position.lng)
1444
+ this.config.center = [this.assets.c8y_Position.lat, this.assets.c8y_Position.lng];
1445
+ this.setView(this.assets.c8y_Position.lat, this.assets.c8y_Position.lng);
1340
1446
  }
1341
1447
  }
1342
- ngOnInit() {
1343
- const isDynamicGroup = !!this.parentGroup && this.assetNodeService.isDynamicGroup(this.parentGroup);
1344
- if (!this.isRootGroup) {
1345
- (isDynamicGroup
1346
- ? this.deviceListExtensionService.items$
1347
- : of(this.subAssetsGridService.getDefaultColumns(this.filterable, this.sortable)))
1348
- .pipe(takeUntil(this.destroyed$))
1349
- .subscribe(columns => (this.columns = columns));
1350
- }
1351
- if (!this.filterable || !this.sortable) {
1352
- this.displayOptions.filter = this.filterable;
1353
- this.columns.forEach(column => {
1354
- column.filterable = this.filterable;
1355
- column.sortable = this.sortable;
1448
+ ngOnChanges(changes) {
1449
+ if (changes.isEdit?.currentValue) {
1450
+ this.showMap = true;
1451
+ this.isMarkerDraggable = true;
1452
+ this.isMapClickable = true;
1453
+ queueMicrotask(() => this.mapView?.map.invalidateSize());
1454
+ this.mapView?.map.on('click', event => {
1455
+ this.onClickOfMap(event);
1456
+ this.updateMarker(event.latlng.lat, event.latlng.lng);
1457
+ });
1458
+ this.formSubscription = this.form?.valueChanges.subscribe(value => {
1459
+ this.updateMarker(value.c8y_Position.lat, value.c8y_Position.lng);
1460
+ this.setView(value.c8y_Position.lat, value.c8y_Position.lng);
1356
1461
  });
1462
+ return;
1357
1463
  }
1358
- this.setActionControls();
1359
- this.showSearch = isDynamicGroup || !this.parentGroup;
1360
- }
1361
- setActionControls() {
1362
- const actionControls = [];
1363
- const unassignAction = {
1364
- type: 'UNASSIGN',
1365
- icon: 'unlink',
1366
- text: gettext('Unassign'),
1367
- priority: 1000,
1368
- callback: (asset) => this.onUnassignAsset(asset, this.parentGroup),
1369
- showIf: (asset) => this.subAssetsGridService.isDevice(asset) &&
1370
- !this.subAssetsGridService.isSmartGroup(this.parentGroup)
1371
- };
1372
- actionControls.push(unassignAction);
1373
- const deleteAction = {
1374
- type: BuiltInActionType.Delete,
1375
- priority: -Infinity,
1376
- callback: (asset) => this.onDeleteAsset(asset, this.parentGroup),
1377
- showIf: (asset) => {
1378
- if (this.smartGroupsService.isSmartGroup(asset)) {
1379
- return this.subAssetsGridService.canDeleteSmartGroup();
1380
- }
1381
- return true;
1464
+ if (!changes.isEdit?.currentValue) {
1465
+ const isAnyValueMissing = this.checkIfAnyValueIsMissing(this.locationMO?.c8y_Position.lat, this.locationMO?.c8y_Position.lng);
1466
+ if (isAnyValueMissing) {
1467
+ this.showMap = false;
1468
+ return;
1382
1469
  }
1383
- };
1384
- actionControls.push(deleteAction);
1385
- if (!this.actionControls) {
1386
- this.actionControls = actionControls;
1470
+ this.isMarkerDraggable = false;
1471
+ this.isMapClickable = false;
1472
+ this.updateMarker(this.locationMO?.c8y_Position.lat, this.locationMO?.c8y_Position.lng);
1473
+ this.setView(this.locationMO?.c8y_Position.lat, this.locationMO?.c8y_Position.lng);
1387
1474
  }
1388
1475
  }
1389
- onUnassignAsset(asset, parentRef) {
1390
- const initialState = {
1391
- asset
1392
- };
1393
- const modalRef = this.bsModalService.show(UnassignModalComponent, { initialState });
1394
- modalRef.content.closeSubject.subscribe(async (result) => {
1395
- if (result) {
1396
- await this.subAssetsGridService.unassignAsset(asset, parentRef);
1397
- this.refresh.emit();
1398
- }
1399
- });
1476
+ ngOnDestroy() {
1477
+ this.formSubscription?.unsubscribe();
1478
+ if (this.mapView?.markers && this.dragListener) {
1479
+ this.mapView?.markers.forEach(marker => {
1480
+ marker.off('drag', this.dragListener);
1481
+ });
1482
+ }
1400
1483
  }
1401
- async onDeleteAsset(asset, parentRef) {
1402
- const initialState = {
1403
- showWithDeviceUserCheckbox: this.subAssetsGridService.shouldShowWithDeviceUserCheckbox(asset),
1404
- asset,
1405
- showWithCascadeCheckbox: !this.smartGroupsService.isSmartGroup(asset)
1406
- };
1407
- const modalRef = this.bsModalService.show(DeleteAssetsModalComponent, { initialState });
1408
- modalRef.content.closeSubject.subscribe(async (result) => {
1409
- if (result) {
1410
- await this.subAssetsGridService.deleteAsset(asset, parentRef, result);
1411
- if (result.cascade) {
1412
- this.showCounterWarning = true;
1484
+ /**
1485
+ * This command is used to prefill the latitude and longitude values in the form when the marker is dragged.
1486
+ */
1487
+ onMarkerDrag(event) {
1488
+ if (this.form) {
1489
+ const properties = this.form.get('c8y_Position');
1490
+ properties?.get('lat').patchValue(event.target._latlng.lat);
1491
+ properties?.get('lng').patchValue(event.target._latlng.lng);
1492
+ }
1493
+ }
1494
+ /**
1495
+ * This method is used to update the marker with the specified values and if any one of the values is not availble, sets
1496
+ * showWarning to true.
1497
+ * @param latitude - The latitude of the marker
1498
+ * @param longitude - The longitude of the marker
1499
+ */
1500
+ updateMarker(latitude, longitude) {
1501
+ const isAnyValueMissing = this.checkIfAnyValueIsMissing(latitude, longitude);
1502
+ if (!isAnyValueMissing) {
1503
+ [latitude, longitude] = this.setLatLngValues(latitude, longitude);
1504
+ const asset = {
1505
+ c8y_Position: {
1506
+ latitude,
1507
+ longitude
1413
1508
  }
1414
- this.refresh.emit();
1509
+ };
1510
+ if (this.mapView) {
1511
+ const icon = this.mapView.getAssetIcon(this.assets);
1512
+ const leafletMarker = this.mapView.leaflet.marker([latitude, longitude], {
1513
+ icon: icon,
1514
+ draggable: this.isMarkerDraggable
1515
+ });
1516
+ if (this.isMarkerDraggable) {
1517
+ this.dragListener = event => {
1518
+ this.onMarkerDrag(event);
1519
+ };
1520
+ leafletMarker.on('dragend', this.dragListener);
1521
+ }
1522
+ this.mapView.clearMarkers();
1523
+ const marker = getC8yMarker(leafletMarker, asset);
1524
+ this.mapView.addMarkerToMap(marker);
1525
+ this.setView(latitude, longitude);
1415
1526
  }
1416
- });
1527
+ return;
1528
+ }
1529
+ this.mapView.clearMarkers();
1417
1530
  }
1418
- ngOnChanges(changes) {
1419
- if (changes.parentGroup && !changes.parentGroup.firstChange) {
1420
- this.dataGrid.reload();
1531
+ /**
1532
+ * This command is used to prefill the latitude and longitude values in the form on click of map.
1533
+ */
1534
+ onClickOfMap(event) {
1535
+ if (this.form) {
1536
+ const properties = this.form.get('c8y_Position');
1537
+ properties?.get('lat').patchValue(event.latlng.lat);
1538
+ properties?.get('lng').patchValue(event.latlng.lng);
1539
+ this.form.markAsDirty();
1421
1540
  }
1422
1541
  }
1423
- trackByName(_index, column) {
1424
- return column.name;
1542
+ /**
1543
+ * Used to enable full screen of the map.
1544
+ */
1545
+ enableFullscreen() {
1546
+ toggleFullscreen(this.mapView.mapElement.nativeElement);
1425
1547
  }
1426
- onReload() {
1427
- this.assetNodeService.rootNode.refresh();
1548
+ /**
1549
+ * Checks if any one of the values i.e., latitude/longitude is undefined or null.
1550
+ * @param latitude Latitude value of the position
1551
+ * @param longitude Longitude value of the position
1552
+ * @returns returns true if any one of the values are both the values are missing else it returns false.
1553
+ */
1554
+ checkIfAnyValueIsMissing(latitude, longitude) {
1555
+ return this.isNullOrUndefined(latitude) || this.isNullOrUndefined(longitude);
1428
1556
  }
1429
- async onDataSourceModifier(dataSourceModifier) {
1430
- const promises = [];
1431
- let counters;
1432
- promises.push(this.subAssetsGridService.getData(dataSourceModifier.columns, dataSourceModifier.pagination, this.parentGroup, this.baseQuery, dataSourceModifier.searchText));
1433
- promises.push(this.subAssetsGridService.getTotal(this.parentGroup, this.baseQuery));
1434
- promises.push(this.subAssetsGridService.getCount(dataSourceModifier.columns, dataSourceModifier.pagination, this.parentGroup, this.baseQuery, dataSourceModifier.searchText));
1435
- const [dataResponse, size, filteredSize] = await Promise.all(promises);
1436
- if (!counters) {
1437
- counters = {
1438
- size,
1439
- filteredSize
1440
- };
1557
+ /**
1558
+ * Sets the view of the map based on the position of marker.
1559
+ * @param latitude - Latitude of the marker
1560
+ * @param longitude Longitude of the marker
1561
+ */
1562
+ setView(latitude, longitude) {
1563
+ if (isNumber(latitude) && isNumber(longitude) && this.mapView) {
1564
+ [latitude, longitude] = this.setLatLngValues(latitude, longitude);
1565
+ this.config.center = [latitude, longitude];
1566
+ this.mapView.center();
1441
1567
  }
1442
- this.onColumnsChange.emit(dataSourceModifier.columns);
1443
- return {
1444
- res: dataResponse.res,
1445
- data: dataResponse.data,
1446
- paging: dataResponse.paging,
1447
- ...counters
1448
- };
1449
1568
  }
1450
- ngOnDestroy() {
1451
- this.destroyed$.next();
1452
- this.destroyed$.complete();
1569
+ setLatLngValues(latitude, longitude) {
1570
+ latitude = this.isNullOrUndefined(latitude) ? defaultMapLocation.lat : latitude;
1571
+ longitude = this.isNullOrUndefined(longitude) ? defaultMapLocation.lng : longitude;
1572
+ return [latitude, longitude];
1453
1573
  }
1454
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridComponent, deps: [{ token: SubAssetsService }, { token: i2$3.BsModalService }, { token: i2.SmartGroupsService }, { token: i4$2.DeviceListExtensionService }, { token: i4.AssetNodeService }], target: i0.ɵɵFactoryTarget.Component }); }
1455
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SubAssetsGridComponent, selector: "c8y-sub-assets-grid", inputs: { parentGroup: ["parent-group", "parentGroup"], refresh: "refresh", title: "title", emptyStateText: "emptyStateText", loadingItemsLabel: "loadingItemsLabel", columnsConfigKey: "columnsConfigKey", columns: "columns", _pagination: ["pagination", "_pagination"], _actionControls: ["actionControls", "_actionControls"], selectable: "selectable", baseQuery: "baseQuery", _bulkActionControls: ["bulkActionControls", "_bulkActionControls"], filterable: "filterable", sortable: "sortable", _displayOptions: ["displayOptions", "_displayOptions"] }, outputs: { onColumnsChange: "onColumnsChange", itemsSelect: "itemsSelect" }, providers: [
1456
- {
1457
- provide: UserPreferencesConfigurationStrategy,
1458
- useClass: UserPreferencesConfigurationStrategy
1459
- },
1460
- {
1461
- provide: SmartGroupGridConfigurationStrategy,
1462
- useClass: SmartGroupGridConfigurationStrategy
1463
- },
1464
- {
1465
- provide: DATA_GRID_CONFIGURATION_STRATEGY,
1466
- useClass: SubAssetsGridConfigurationStrategy
1467
- },
1468
- {
1469
- provide: DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER,
1470
- useExisting: SubAssetsGridComponent
1471
- }
1472
- ], viewQueries: [{ propertyName: "dataGrid", first: true, predicate: DataGridComponent, descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<c8y-data-grid\n [title]=\"title\"\n [loadingItemsLabel]=\"loadingItemsLabel\"\n [columns]=\"columns\"\n [pagination]=\"pagination\"\n [actionControls]=\"actionControls\"\n [selectable]=\"selectable\"\n [bulkActionControls]=\"bulkActionControls\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [infiniteScroll]=\"getInfiniteScrollMode\"\n [showCounterWarning]=\"showCounterWarning\"\n [refresh]=\"refresh\"\n [showSearch]=\"showSearch\"\n [displayOptions]=\"displayOptions\"\n (itemsSelect)=\"itemsSelect.emit($event)\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n (onReload)=\"onReload()\"\n>\n <c8y-ui-empty-state\n [icon]=\"'c8y-group-add'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (emptyStateText | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n\n <ng-container *ngFor=\"let column of columns; trackBy: trackByName\">\n <c8y-column [name]=\"column.name\"></c8y-column>\n </ng-container>\n</c8y-data-grid>\n", dependencies: [{ kind: "component", type: i3.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i3.EmptyStateContextDirective, selector: "[emptyStateContext]" }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.ColumnDirective, selector: "c8y-column", inputs: ["name"] }, { kind: "component", type: i3.DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "columns", "rows", "pagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "directive", type: i3.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
1574
+ isNullOrUndefined(value) {
1575
+ return value === null || value === undefined;
1576
+ }
1577
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetLocationComponent, deps: [{ token: i1$2.ActivatedRoute }, { token: i2$3.MapService }], target: i0.ɵɵFactoryTarget.Component }); }
1578
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AssetLocationComponent, selector: "c8y-asset-location", inputs: { isEdit: "isEdit", locationMO: "locationMO", form: "form" }, viewQueries: [{ propertyName: "mapView", first: true, predicate: MapComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div [hidden]=\"!showMap\">\n <div class=\"row\">\n <button\n class=\"btn btn-link pull-right\"\n style=\"margin-right: 12px\"\n title=\"Full screen\"\n type=\"button\"\n data-cy=\"asset-location-full-screen\"\n (click)=\"enableFullscreen()\"\n >\n <i c8yIcon=\"expand\"></i>\n </button>\n </div>\n <div style=\"width: 100%; height: 400px\">\n <c8y-map\n #map\n [assets]=\"assets\"\n [config]=\"config\"\n ></c8y-map>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: i2$3.MapComponent, selector: "c8y-map", inputs: ["config", "assets", "polyline$", "polylineOptions"], outputs: ["onRealtimeUpdate", "onMove", "onMoveEnd", "onZoomStart", "onZoomEnd", "onMap", "onInit"] }] }); }
1473
1579
  }
1474
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsGridComponent, decorators: [{
1580
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetLocationComponent, decorators: [{
1475
1581
  type: Component,
1476
- args: [{ selector: 'c8y-sub-assets-grid', providers: [
1477
- {
1478
- provide: UserPreferencesConfigurationStrategy,
1479
- useClass: UserPreferencesConfigurationStrategy
1480
- },
1481
- {
1482
- provide: SmartGroupGridConfigurationStrategy,
1483
- useClass: SmartGroupGridConfigurationStrategy
1484
- },
1485
- {
1486
- provide: DATA_GRID_CONFIGURATION_STRATEGY,
1487
- useClass: SubAssetsGridConfigurationStrategy
1488
- },
1489
- {
1490
- provide: DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER,
1491
- useExisting: SubAssetsGridComponent
1492
- }
1493
- ], template: "<c8y-data-grid\n [title]=\"title\"\n [loadingItemsLabel]=\"loadingItemsLabel\"\n [columns]=\"columns\"\n [pagination]=\"pagination\"\n [actionControls]=\"actionControls\"\n [selectable]=\"selectable\"\n [bulkActionControls]=\"bulkActionControls\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [infiniteScroll]=\"getInfiniteScrollMode\"\n [showCounterWarning]=\"showCounterWarning\"\n [refresh]=\"refresh\"\n [showSearch]=\"showSearch\"\n [displayOptions]=\"displayOptions\"\n (itemsSelect)=\"itemsSelect.emit($event)\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n (onReload)=\"onReload()\"\n>\n <c8y-ui-empty-state\n [icon]=\"'c8y-group-add'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (emptyStateText | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n\n <ng-container *ngFor=\"let column of columns; trackBy: trackByName\">\n <c8y-column [name]=\"column.name\"></c8y-column>\n </ng-container>\n</c8y-data-grid>\n" }]
1494
- }], ctorParameters: () => [{ type: SubAssetsService }, { type: i2$3.BsModalService }, { type: i2.SmartGroupsService }, { type: i4$2.DeviceListExtensionService }, { type: i4.AssetNodeService }], propDecorators: { parentGroup: [{
1495
- type: Input,
1496
- args: ['parent-group']
1497
- }], refresh: [{
1498
- type: Input
1499
- }], title: [{
1500
- type: Input
1501
- }], emptyStateText: [{
1502
- type: Input
1503
- }], loadingItemsLabel: [{
1504
- type: Input
1505
- }], columnsConfigKey: [{
1506
- type: Input
1507
- }], columns: [{
1508
- type: Input
1509
- }], _pagination: [{
1510
- type: Input,
1511
- args: ['pagination']
1512
- }], _actionControls: [{
1513
- type: Input,
1514
- args: ['actionControls']
1515
- }], selectable: [{
1516
- type: Input
1517
- }], baseQuery: [{
1582
+ args: [{ selector: 'c8y-asset-location', template: "<div [hidden]=\"!showMap\">\n <div class=\"row\">\n <button\n class=\"btn btn-link pull-right\"\n style=\"margin-right: 12px\"\n title=\"Full screen\"\n type=\"button\"\n data-cy=\"asset-location-full-screen\"\n (click)=\"enableFullscreen()\"\n >\n <i c8yIcon=\"expand\"></i>\n </button>\n </div>\n <div style=\"width: 100%; height: 400px\">\n <c8y-map\n #map\n [assets]=\"assets\"\n [config]=\"config\"\n ></c8y-map>\n </div>\n</div>\n" }]
1583
+ }], ctorParameters: () => [{ type: i1$2.ActivatedRoute }, { type: i2$3.MapService }], propDecorators: { mapView: [{
1584
+ type: ViewChild,
1585
+ args: [MapComponent]
1586
+ }], isEdit: [{
1518
1587
  type: Input
1519
- }], _bulkActionControls: [{
1520
- type: Input,
1521
- args: ['bulkActionControls']
1522
- }], filterable: [{
1588
+ }], locationMO: [{
1523
1589
  type: Input
1524
- }], sortable: [{
1590
+ }], form: [{
1525
1591
  type: Input
1526
- }], onColumnsChange: [{
1527
- type: Output
1528
- }], itemsSelect: [{
1529
- type: Output
1530
- }], dataGrid: [{
1531
- type: ViewChild,
1532
- args: [DataGridComponent, { static: true }]
1533
- }], _displayOptions: [{
1534
- type: Input,
1535
- args: ['displayOptions']
1536
1592
  }] } });
1537
1593
 
1538
- class AssignChildDevicesComponent {
1539
- constructor(alert, subAssetsService, inventoryService) {
1594
+ class AssetPropertiesComponent {
1595
+ constructor(assetTypes, inventory, inventoryBinary, alert) {
1596
+ this.assetTypes = assetTypes;
1597
+ this.inventory = inventory;
1598
+ this.inventoryBinary = inventoryBinary;
1540
1599
  this.alert = alert;
1541
- this.subAssetsService = subAssetsService;
1542
- this.inventoryService = inventoryService;
1543
- this.onCancel = new EventEmitter();
1544
- this.refresh = new EventEmitter();
1545
- this.selected = [];
1546
- this.canAssignDevice = false;
1547
- this.pendingStatus = false;
1600
+ this.assetChange = new EventEmitter();
1601
+ this.properties = [];
1602
+ this.customProperties = [];
1603
+ this.isEdit = false;
1604
+ this.isLoading = false;
1548
1605
  }
1549
- onEnterKeyDown(_event) {
1550
- if (this.selected.length > 0) {
1551
- this.assignDevices();
1606
+ ngOnChanges(changes) {
1607
+ if (changes.asset) {
1608
+ // Back button handling, as component is not destroyed
1609
+ this.assetType = undefined;
1610
+ this.customProperties = [];
1611
+ this.loadAsset();
1552
1612
  }
1553
1613
  }
1554
- onEscapeKeyDown(_event) {
1555
- this.onCancel.emit();
1614
+ async loadAsset() {
1615
+ this.isLoading = true;
1616
+ const assetType$ = this.assetTypes.getAssetTypeByName$(this.asset.type);
1617
+ this.assetType = await firstValueFrom(assetType$);
1618
+ try {
1619
+ this.properties = this.keepOrder(this.assetType?.c8y_IsAssetType?.properties, this.properties);
1620
+ }
1621
+ catch (ex) {
1622
+ console.warn(ex);
1623
+ }
1624
+ this.customProperties = await this.resolveCustomProperties(this.properties);
1625
+ this.isLoading = false;
1556
1626
  }
1557
- async ngOnInit() {
1558
- this.setNotIncludedInGroupQuery();
1559
- this.canAssignDevice = await this.subAssetsService.canAssignDevice({
1560
- id: this.currentGroupId
1561
- });
1627
+ async resolveCustomProperties(managedObjects) {
1628
+ const properties = [];
1629
+ for (const mo of managedObjects) {
1630
+ if (mo.c8y_JsonSchema) {
1631
+ const [item] = await this.parseItem(mo, mo.c8y_JsonSchema.properties, this.asset);
1632
+ this.setItemRequired(item, mo);
1633
+ properties.push(item);
1634
+ }
1635
+ }
1636
+ return properties;
1637
+ }
1638
+ deleteTitleFromMOJsonSchema(mo) {
1639
+ const schemaProperties = mo?.c8y_JsonSchema?.properties;
1640
+ const property = Object.keys(schemaProperties || {})[0];
1641
+ delete (mo?.c8y_JsonSchema?.properties[property] || {}).title;
1642
+ }
1643
+ /**
1644
+ * This method is used to order the complex properties in the order specified by the user in asset properties screen.
1645
+ * @param mo - Managed object of the complex property associated with the asset.
1646
+ */
1647
+ orderComplexProperties(mo) {
1648
+ const complexProperties = mo.c8y_JsonSchema.properties[mo.name]?.['properties'];
1649
+ const keyValuesArray = toPairs(complexProperties);
1650
+ const orderedProperties = sortBy(keyValuesArray, ([, value]) => value.order);
1651
+ mo.c8y_JsonSchema.properties[mo.name]['properties'] = fromPairs(orderedProperties);
1652
+ }
1653
+ async parseItem(mo, properties, asset) {
1654
+ if (!asset) {
1655
+ return [];
1656
+ }
1657
+ const keys = Object.keys(properties);
1658
+ const items = [];
1659
+ for (const key of keys) {
1660
+ const type = properties[key].type;
1661
+ const title = properties[key].title;
1662
+ let value = this.getTypeValue(type, asset[key]);
1663
+ let file;
1664
+ if (type === 'file' && value) {
1665
+ const fileId = typeof value === 'object' ? value[0]?.file?.id : value;
1666
+ const fileData = await this.getFileManagedObject(fileId);
1667
+ file = fileData;
1668
+ value = [fileData];
1669
+ }
1670
+ else if (type === 'date') {
1671
+ const valueDate = new Date(value);
1672
+ value = !isNaN(valueDate.getTime()) ? valueDate : '';
1673
+ }
1674
+ if (type === 'object') {
1675
+ // remove title to avoid excessive property name on asset complex properties form
1676
+ this.deleteTitleFromMOJsonSchema(mo);
1677
+ this.orderComplexProperties(mo);
1678
+ if (!value) {
1679
+ value = {};
1680
+ for (const prop in properties[key].properties) {
1681
+ value[prop] = this.getTypeValue(properties[key].properties[prop].type, null);
1682
+ }
1683
+ }
1684
+ }
1685
+ items.push({
1686
+ key,
1687
+ value,
1688
+ label: title || mo.label,
1689
+ type,
1690
+ description: mo.description,
1691
+ file,
1692
+ complex: type === 'object'
1693
+ ? await this.parseItem(mo, properties[key].properties, value)
1694
+ : undefined,
1695
+ isEdit: false,
1696
+ jsonSchema: mo.c8y_JsonSchema
1697
+ });
1698
+ }
1699
+ return items;
1562
1700
  }
1563
- setNotIncludedInGroupQuery() {
1564
- const notIncludedInGroupQuery = { __not: { __bygroupid: this.currentGroupId } };
1565
- this.baseQuery = notIncludedInGroupQuery;
1701
+ toggleEdit(prop) {
1702
+ prop.isEdit = !prop.isEdit;
1566
1703
  }
1567
- async assignDevices() {
1568
- if (this.canAssignDevice === false) {
1569
- return;
1570
- }
1571
- this.pendingStatus = true;
1704
+ async getFileManagedObject(id) {
1572
1705
  try {
1573
- await this.inventoryService.childAssetsBulkAdd(this.selected, this.currentGroupId);
1574
- this.refresh.emit();
1575
- this.alert.success(gettext('Child devices assigned.'));
1706
+ const { data } = await this.inventory.detail(id);
1707
+ return data;
1576
1708
  }
1577
- catch (error) {
1578
- this.alert.danger(gettext('Could not assign child devices.'), error);
1709
+ catch (ex) {
1710
+ this.alert.addServerFailure(ex);
1579
1711
  }
1580
- this.pendingStatus = false;
1581
- this.selected = [];
1582
- this.onCancel.emit();
1583
1712
  }
1584
- onSelected(selectedDevicesIDs) {
1585
- this.selected = selectedDevicesIDs;
1713
+ async save(propertyValue, prop) {
1714
+ try {
1715
+ if (prop.type === 'object') {
1716
+ this.updateUndefinedToPropTypeValue(prop, propertyValue[prop.key]);
1717
+ }
1718
+ else {
1719
+ this.updateUndefinedToPropTypeValue(prop, propertyValue);
1720
+ }
1721
+ propertyValue = await this.uploadFiles(propertyValue, prop.value);
1722
+ // Avoid making a PUT request containing just the id, as response body might be incomplete
1723
+ const hasValues = Object.values(propertyValue).some(value => value !== undefined);
1724
+ if (!hasValues) {
1725
+ this.toggleEdit(prop);
1726
+ return;
1727
+ }
1728
+ const updatedAsset = { id: this.asset.id, ...propertyValue };
1729
+ const { data } = await this.inventory.update(updatedAsset);
1730
+ this.toggleEdit(prop);
1731
+ this.asset = data;
1732
+ this.assetChange.emit(this.asset);
1733
+ await this.loadAsset();
1734
+ this.alert.success(gettext('Asset properties updated.'));
1735
+ }
1736
+ catch (ex) {
1737
+ this.alert.addServerFailure(ex);
1738
+ this.toggleEdit(prop);
1739
+ }
1586
1740
  }
1587
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssignChildDevicesComponent, deps: [{ token: i3.AlertService }, { token: SubAssetsService }, { token: i2.InventoryService }], target: i0.ɵɵFactoryTarget.Component }); }
1588
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AssignChildDevicesComponent, selector: "c8y-assign-child-devices", inputs: { currentGroupId: "currentGroupId", parentDevice: "parentDevice", refresh: "refresh" }, outputs: { onCancel: "onCancel" }, host: { listeners: { "document:keydown.enter": "onEnterKeyDown($event)", "document:keydown.escape": "onEscapeKeyDown($event)" } }, ngImport: i0, template: "<div class=\"card-block flex-no-shrink separator-bottom col-xs-12 large-padding p-t-24 p-b-24\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <h4 class=\"text-center text-medium\">\n {{ 'Assign child devices' | translate }}\n </h4>\n </div>\n </div>\n</div>\n\n<c8y-sub-assets-grid\n [title]=\"''\"\n [emptyStateText]=\"'All child devices are already assigned' | translate\"\n [refresh]=\"refresh\"\n [actionControls]=\"[]\"\n [columnsConfigKey]=\"'assign-child-devices'\"\n [selectable]=\"true\"\n [parent-group]=\"parentDevice\"\n [baseQuery]=\"baseQuery\"\n (itemsSelect)=\"onSelected($event)\"\n class=\"d-contents\"\n></c8y-sub-assets-grid>\n\n<div class=\"text-center card-footer p-24 separator\">\n <button\n (click)=\"onCancel.emit()\"\n type=\"button\"\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n >\n <span>{{ 'Cancel' | translate }}</span>\n </button>\n <button\n (click)=\"assignDevices()\"\n type=\"button\"\n class=\"btn btn-primary\"\n [ngClass]=\"{ 'btn-pending': pendingStatus }\"\n title=\"{{ 'Assign' | translate }}\"\n [disabled]=\"selected.length === 0 || !canAssignDevice\"\n >\n <span>{{ 'Assign' | translate }}</span>\n </button>\n</div>\n", dependencies: [{ kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: SubAssetsGridComponent, selector: "c8y-sub-assets-grid", inputs: ["parent-group", "refresh", "title", "emptyStateText", "loadingItemsLabel", "columnsConfigKey", "columns", "pagination", "actionControls", "selectable", "baseQuery", "bulkActionControls", "filterable", "sortable", "displayOptions"], outputs: ["onColumnsChange", "itemsSelect"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
1589
- }
1590
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssignChildDevicesComponent, decorators: [{
1591
- type: Component,
1592
- args: [{ selector: 'c8y-assign-child-devices', template: "<div class=\"card-block flex-no-shrink separator-bottom col-xs-12 large-padding p-t-24 p-b-24\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <h4 class=\"text-center text-medium\">\n {{ 'Assign child devices' | translate }}\n </h4>\n </div>\n </div>\n</div>\n\n<c8y-sub-assets-grid\n [title]=\"''\"\n [emptyStateText]=\"'All child devices are already assigned' | translate\"\n [refresh]=\"refresh\"\n [actionControls]=\"[]\"\n [columnsConfigKey]=\"'assign-child-devices'\"\n [selectable]=\"true\"\n [parent-group]=\"parentDevice\"\n [baseQuery]=\"baseQuery\"\n (itemsSelect)=\"onSelected($event)\"\n class=\"d-contents\"\n></c8y-sub-assets-grid>\n\n<div class=\"text-center card-footer p-24 separator\">\n <button\n (click)=\"onCancel.emit()\"\n type=\"button\"\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n >\n <span>{{ 'Cancel' | translate }}</span>\n </button>\n <button\n (click)=\"assignDevices()\"\n type=\"button\"\n class=\"btn btn-primary\"\n [ngClass]=\"{ 'btn-pending': pendingStatus }\"\n title=\"{{ 'Assign' | translate }}\"\n [disabled]=\"selected.length === 0 || !canAssignDevice\"\n >\n <span>{{ 'Assign' | translate }}</span>\n </button>\n</div>\n" }]
1593
- }], ctorParameters: () => [{ type: i3.AlertService }, { type: SubAssetsService }, { type: i2.InventoryService }], propDecorators: { currentGroupId: [{
1594
- type: Input
1595
- }], parentDevice: [{
1596
- type: Input
1597
- }], onCancel: [{
1598
- type: Output
1599
- }], refresh: [{
1600
- type: Input
1601
- }], onEnterKeyDown: [{
1602
- type: HostListener,
1603
- args: ['document:keydown.enter', ['$event']]
1604
- }], onEscapeKeyDown: [{
1605
- type: HostListener,
1606
- args: ['document:keydown.escape', ['$event']]
1607
- }] } });
1608
-
1609
- class AssignDevicesComponent {
1610
- static { this.GRID_CONFIG_CONTEXT = {
1611
- key: 'assign-devices-grid',
1612
- configFilter: {
1613
- filter: false
1741
+ updateUndefinedToPropTypeValue(prop, propertyValue) {
1742
+ for (const [key, value] of Object.entries(propertyValue)) {
1743
+ const property = prop.complex ? find(prop.complex, { key: key }) : prop;
1744
+ propertyValue[key] = this.getTypeValue(property.type, value);
1614
1745
  }
1615
- }; }
1616
- constructor(alert, subAssetsService, inventoryService, gainsightService) {
1617
- this.alert = alert;
1618
- this.subAssetsService = subAssetsService;
1619
- this.inventoryService = inventoryService;
1620
- this.gainsightService = gainsightService;
1621
- this.CURRENT_LOCATION = location.href;
1622
- this.PRODUCT_EXPERIENCE = PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED;
1623
- this.refresh = new EventEmitter();
1624
- this.onCancel = new EventEmitter();
1625
- this.onShowChildDevices = new EventEmitter();
1626
- this.selectedDevice = new EventEmitter();
1627
- this.pendingStatus = false;
1628
- this.pagination = { pageSize: 20, currentPage: 1 };
1629
- this.selected = [];
1630
- this.canAssignDevice = false;
1631
- this.actionControls = [];
1632
- this.headerActionControls = [];
1633
- this.showChildren = false;
1634
- this.isSelectable = true;
1635
1746
  }
1636
- onEnterKeyDown(_event) {
1637
- if (this.selected.length > 0) {
1638
- this.assignDevices();
1747
+ getTypeValue(propType, value) {
1748
+ if (value || (propType === 'boolean' && value !== undefined))
1749
+ return value;
1750
+ switch (propType) {
1751
+ case 'number':
1752
+ case 'boolean':
1753
+ return value || value === 0 ? value : null;
1754
+ default:
1755
+ return '';
1639
1756
  }
1640
1757
  }
1641
- async ngOnInit() {
1642
- this.setNotIncludedInGroupQuery();
1643
- this.canAssignDevice = await this.subAssetsService.canAssignDevice({
1644
- id: this.currentGroupId
1758
+ keepOrder(correctOrderedIds, properties) {
1759
+ const orderedProperties = correctOrderedIds.map(({ id }) => {
1760
+ const foundProperty = properties.find(property => property.id === id);
1761
+ if (!foundProperty) {
1762
+ throw new Error('Custom property mismatch');
1763
+ }
1764
+ return foundProperty;
1645
1765
  });
1646
- this.setHeaderActionControls();
1647
- }
1648
- setNotIncludedInGroupQuery() {
1649
- const notIncludedInGroupQuery = { __not: { __bygroupid: this.currentGroupId } };
1650
- this.baseQuery = notIncludedInGroupQuery;
1766
+ return orderedProperties;
1651
1767
  }
1652
- setHeaderActionControls() {
1653
- const headerActionControls = [];
1654
- const showChildDevices = {
1655
- type: 'DISPLAY_CHILD_DEVICES_BUTTON',
1656
- text: gettext('Enable child devices selection'),
1657
- template: this.showDevicesToggle,
1658
- callback: () => {
1659
- this.showChildren = !this.showChildren;
1660
- this.setActionControls(this.showChildren);
1661
- this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.EVENT, {
1662
- component: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES,
1663
- action: PRODUCT_EXPERIENCE_SUB_ASSETS_SHARED.ASSIGN_DEVICES.ACTIONS.DISPLAY_CHILD_DEVICES,
1664
- url: this.CURRENT_LOCATION
1665
- });
1768
+ async uploadFiles(model, moId) {
1769
+ const keys = Object.keys(model);
1770
+ for (const key of keys) {
1771
+ if (Array.isArray(model[key]) && model[key][0]?.file instanceof File) {
1772
+ try {
1773
+ const upload = await this.inventoryBinary.create(model[key][0].file);
1774
+ try {
1775
+ if (moId && moId[0]) {
1776
+ await this.inventory.childAdditionsRemove(moId[0], this.asset.id);
1777
+ }
1778
+ }
1779
+ catch (ex) {
1780
+ throw ex;
1781
+ }
1782
+ model[key] = upload.data.id;
1783
+ await this.inventory.childAdditionsAdd(upload.data.id, this.asset.id);
1784
+ }
1785
+ catch (ex) {
1786
+ throw ex;
1787
+ }
1666
1788
  }
1667
- };
1668
- headerActionControls.push(showChildDevices);
1669
- this.headerActionControls = headerActionControls;
1670
- }
1671
- setActionControls(showChildren) {
1672
- const actionControls = [];
1673
- const selectChildrenAction = {
1674
- type: 'SHOW_TARGET_CHILD_DEVICES',
1675
- icon: 'enter-bottom',
1676
- text: gettext('Select target child devices'),
1677
- callback: (asset) => this.selectChildren(asset),
1678
- showIf: (asset) => asset.childDevices.references.length > 0
1679
- };
1680
- if (showChildren) {
1681
- actionControls.push(selectChildrenAction);
1682
1789
  }
1683
- this.actionControls = actionControls;
1684
- this.refresh.emit();
1790
+ return model;
1685
1791
  }
1686
- async assignDevices() {
1687
- if (this.canAssignDevice === false) {
1792
+ setItemRequired(item, mo) {
1793
+ const isAssetPropertyRequired = !!this.assetType?.c8y_IsAssetType?.properties.find(p => p.id === mo.id)?.isRequired;
1794
+ if (!isAssetPropertyRequired) {
1688
1795
  return;
1689
1796
  }
1690
- this.pendingStatus = true;
1691
- try {
1692
- await this.inventoryService.childAssetsBulkAdd(this.selected, this.currentGroupId);
1693
- this.refresh.emit();
1694
- this.alert.success(gettext('Devices assigned.'));
1797
+ const isComplexProperty = !!item?.complex?.length;
1798
+ if (isComplexProperty) {
1799
+ const complexProperty = item.jsonSchema?.properties?.[mo.c8y_JsonSchema.key];
1800
+ complexProperty.required = item.complex.map(({ key }) => key);
1695
1801
  }
1696
- catch (error) {
1697
- this.alert.danger(gettext('Could not assign devices.'), error);
1802
+ else {
1803
+ item.jsonSchema.required = [mo.c8y_JsonSchema.key];
1698
1804
  }
1699
- this.pendingStatus = false;
1700
- this.selected = [];
1701
- this.onCancel.emit();
1702
- }
1703
- onSelected(selectedDevicesIDs) {
1704
- this.selected = selectedDevicesIDs;
1705
- }
1706
- selectChildren(asset) {
1707
- this.onShowChildDevices.emit(true);
1708
- this.selectedDevice.emit(asset);
1709
1805
  }
1710
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssignDevicesComponent, deps: [{ token: i3.AlertService }, { token: SubAssetsService }, { token: i2.InventoryService }, { token: i3.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
1711
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AssignDevicesComponent, selector: "c8y-assign-devices", inputs: { currentGroupId: "currentGroupId", refresh: "refresh" }, outputs: { onCancel: "onCancel", onShowChildDevices: "onShowChildDevices", selectedDevice: "selectedDevice" }, host: { listeners: { "document:keydown.enter": "onEnterKeyDown($event)" } }, providers: [
1712
- {
1713
- provide: DATA_GRID_CONFIGURATION_STRATEGY,
1714
- useClass: UserPreferencesConfigurationStrategy
1715
- },
1716
- {
1717
- provide: DATA_GRID_CONFIGURATION_CONTEXT,
1718
- useValue: AssignDevicesComponent.GRID_CONFIG_CONTEXT
1719
- }
1720
- ], viewQueries: [{ propertyName: "showDevicesToggle", first: true, predicate: ["showDevicesToggle"], descendants: true, read: TemplateRef }], ngImport: i0, template: "<div class=\"card-block flex-no-shrink separator-bottom col-xs-12 large-padding p-t-24 p-b-24\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'Assign devices' | translate }}\n </div>\n </div>\n </div>\n</div>\n<c8y-device-grid\n class=\"flex-grow col-xs-12 no-gutter\"\n [title]=\"''\"\n [actionControls]=\"actionControls\"\n [infiniteScroll]=\"'auto'\"\n [selectable]=\"isSelectable\"\n [pagination]=\"pagination\"\n (itemsSelect)=\"onSelected($event)\"\n [refresh]=\"refresh\"\n [baseQuery]=\"baseQuery\"\n [headerActionControls]=\"headerActionControls\"\n [withChildren]=\"true\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES }\"\n></c8y-device-grid>\n\n<div class=\"text-center card-footer p-24 separator\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"onCancel.emit()\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES,\n action: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.ACTIONS.CANCEL\n }\"\n >\n <span>{{ 'Cancel' | translate }}</span>\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Assign' | translate }}\"\n type=\"button\"\n [ngClass]=\"{ 'btn-pending': pendingStatus }\"\n (click)=\"assignDevices()\"\n [disabled]=\"selected.length === 0 || !canAssignDevice\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES,\n action: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.ACTIONS.ASSIGN\n }\"\n >\n <span>{{ 'Assign' | translate }}</span>\n </button>\n</div>\n\n<ng-template\n #showDevicesToggle\n let-control=\"headerActionControl\"\n>\n <label\n class=\"c8y-switch a-s-center\"\n title=\"{{ control.text | translate }}\"\n >\n <input\n type=\"checkbox\"\n [(ngModel)]=\"showChildren\"\n (click)=\"control.callback()\"\n />\n <span></span>\n <span>{{ control.text | translate }}</span>\n </label>\n <button\n class=\"btn-help m-r-16 a-s-center\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"childDevicesPop\"\n placement=\"bottom\"\n triggers=\"focus\"\n type=\"button\"\n ></button>\n <ng-template #childDevicesPop>\n <span translate>\n Displays the button\n <span\n class=\"btn btn-dot btn-icon no-pointer\"\n title=\"Child devices icon\"\n >\n <i class=\"text-primary dlt-c8y-icon-enter-bottom\"></i>\n </span>\n next to target devices with children. Clicking it displays a list with all child devices of\n the selected target device.\n </span>\n </ng-template>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "component", type: i7.DeviceGridComponent, selector: "c8y-device-grid", inputs: ["dataCallback", "refresh", "title", "loadMoreItemsLabel", "loadingItemsLabel", "legacyConfigKey", "legacyFilterKey", "columns", "pagination", "infiniteScroll", "actionControls", "selectable", "singleSelection", "baseQuery", "bulkActionControls", "headerActionControls", "childDeviceGrid", "parentDeviceId", "withChildren", "showSearch", "activeClassName"], outputs: ["onColumnsChange", "onFilterChange", "onDeviceQueryStringChange", "itemsSelect"] }, { kind: "directive", type: i7$1.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
1806
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetPropertiesComponent, deps: [{ token: i3.AssetTypesRealtimeService }, { token: i2.InventoryService }, { token: i2.InventoryBinaryService }, { token: i3.AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
1807
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AssetPropertiesComponent, 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 === 'c8y_Position'\">\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: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: i4$2.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: "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: i3.C8yTranslatePipe, name: "translate" }] }); }
1721
1808
  }
1722
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssignDevicesComponent, decorators: [{
1809
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AssetPropertiesComponent, decorators: [{
1723
1810
  type: Component,
1724
- args: [{ selector: 'c8y-assign-devices', providers: [
1725
- {
1726
- provide: DATA_GRID_CONFIGURATION_STRATEGY,
1727
- useClass: UserPreferencesConfigurationStrategy
1728
- },
1729
- {
1730
- provide: DATA_GRID_CONFIGURATION_CONTEXT,
1731
- useValue: AssignDevicesComponent.GRID_CONFIG_CONTEXT
1732
- }
1733
- ], template: "<div class=\"card-block flex-no-shrink separator-bottom col-xs-12 large-padding p-t-24 p-b-24\">\n <div class=\"row\">\n <div class=\"col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4\">\n <div class=\"h4 text-center text-medium\">\n {{ 'Assign devices' | translate }}\n </div>\n </div>\n </div>\n</div>\n<c8y-device-grid\n class=\"flex-grow col-xs-12 no-gutter\"\n [title]=\"''\"\n [actionControls]=\"actionControls\"\n [infiniteScroll]=\"'auto'\"\n [selectable]=\"isSelectable\"\n [pagination]=\"pagination\"\n (itemsSelect)=\"onSelected($event)\"\n [refresh]=\"refresh\"\n [baseQuery]=\"baseQuery\"\n [headerActionControls]=\"headerActionControls\"\n [withChildren]=\"true\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES }\"\n></c8y-device-grid>\n\n<div class=\"text-center card-footer p-24 separator\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"onCancel.emit()\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES,\n action: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.ACTIONS.CANCEL\n }\"\n >\n <span>{{ 'Cancel' | translate }}</span>\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Assign' | translate }}\"\n type=\"button\"\n [ngClass]=\"{ 'btn-pending': pendingStatus }\"\n (click)=\"assignDevices()\"\n [disabled]=\"selected.length === 0 || !canAssignDevice\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE.EVENT\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.COMPONENTS.ASSIGN_DEVICES,\n action: PRODUCT_EXPERIENCE.ASSIGN_DEVICES.ACTIONS.ASSIGN\n }\"\n >\n <span>{{ 'Assign' | translate }}</span>\n </button>\n</div>\n\n<ng-template\n #showDevicesToggle\n let-control=\"headerActionControl\"\n>\n <label\n class=\"c8y-switch a-s-center\"\n title=\"{{ control.text | translate }}\"\n >\n <input\n type=\"checkbox\"\n [(ngModel)]=\"showChildren\"\n (click)=\"control.callback()\"\n />\n <span></span>\n <span>{{ control.text | translate }}</span>\n </label>\n <button\n class=\"btn-help m-r-16 a-s-center\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"childDevicesPop\"\n placement=\"bottom\"\n triggers=\"focus\"\n type=\"button\"\n ></button>\n <ng-template #childDevicesPop>\n <span translate>\n Displays the button\n <span\n class=\"btn btn-dot btn-icon no-pointer\"\n title=\"Child devices icon\"\n >\n <i class=\"text-primary dlt-c8y-icon-enter-bottom\"></i>\n </span>\n next to target devices with children. Clicking it displays a list with all child devices of\n the selected target device.\n </span>\n </ng-template>\n</ng-template>\n" }]
1734
- }], ctorParameters: () => [{ type: i3.AlertService }, { type: SubAssetsService }, { type: i2.InventoryService }, { type: i3.GainsightService }], propDecorators: { currentGroupId: [{
1735
- type: Input
1736
- }], refresh: [{
1811
+ args: [{ selector: 'c8y-asset-properties', 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 === 'c8y_Position'\">\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" }]
1812
+ }], ctorParameters: () => [{ type: i3.AssetTypesRealtimeService }, { type: i2.InventoryService }, { type: i2.InventoryBinaryService }, { type: i3.AlertService }], propDecorators: { asset: [{
1737
1813
  type: Input
1738
- }], onCancel: [{
1739
- type: Output
1740
- }], onShowChildDevices: [{
1741
- type: Output
1742
- }], selectedDevice: [{
1814
+ }], assetChange: [{
1743
1815
  type: Output
1744
- }], showDevicesToggle: [{
1745
- type: ViewChild,
1746
- args: ['showDevicesToggle', { read: TemplateRef }]
1747
- }], onEnterKeyDown: [{
1748
- type: HostListener,
1749
- args: ['document:keydown.enter', ['$event']]
1816
+ }], properties: [{
1817
+ type: Input
1750
1818
  }] } });
1751
1819
 
1752
1820
  const SUB_ASSETS_CONFIG = new InjectionToken('SubAssetsConfig');
@@ -1870,13 +1938,13 @@ class GroupInfoComponent {
1870
1938
  throw Error(error);
1871
1939
  }
1872
1940
  }
1873
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", 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$2.DeviceListExtensionService }, { token: SUB_ASSETS_CONFIG }], target: i0.ɵɵFactoryTarget.Component }); }
1941
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", 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 }); }
1874
1942
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: GroupInfoComponent, 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: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.TextareaAutoresizeDirective, selector: "[c8y-textarea-autoresize]" }, { 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: i3.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i3.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i3.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "directive", type: i7$1.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: i9.BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: i9.BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: i9.BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i2$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.DatePipe, name: "c8yDate" }, { kind: "pipe", type: i3.FilterMapperPipe, name: "mapToFilterChips" }, { kind: "pipe", type: i3.GroupedFilterChips, name: "groupedFilterChips" }] }); }
1875
1943
  }
1876
1944
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: GroupInfoComponent, decorators: [{
1877
1945
  type: Component,
1878
1946
  args: [{ selector: 'c8y-group-info', 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" }]
1879
- }], ctorParameters: () => [{ type: i2.InventoryService }, { type: SubAssetsService }, { type: i2.SmartGroupsService }, { type: i3.AlertService }, { type: i3.ModalService }, { type: i4.AssetNodeService }, { type: i3.AssetTypesRealtimeService }, { type: i4$2.DeviceListExtensionService }, { type: undefined, decorators: [{
1947
+ }], 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: [{
1880
1948
  type: Inject,
1881
1949
  args: [SUB_ASSETS_CONFIG]
1882
1950
  }] }], propDecorators: { group: [{
@@ -1944,11 +2012,11 @@ class GroupsComponent {
1944
2012
  });
1945
2013
  }
1946
2014
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: GroupsComponent, deps: [{ token: i3.Permissions }, { token: SubAssetsService }, { token: SUB_ASSETS_CONFIG }, { token: i1$2.ActivatedRoute }, { token: i1$2.Router }], target: i0.ɵɵFactoryTarget.Component }); }
1947
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: GroupsComponent, selector: "c8y-groups-name", ngImport: i0, template: "<c8y-title>\n {{ moduleConfig.name | translate }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-group-open\"\n label=\"{{ moduleConfig.name | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n [placement]=\"'right'\"\n *ngIf=\"moduleConfig.showAddGroupBtn\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Add group' | translate }}\"\n (click)=\"onAddGroupClick()\"\n [disabled]=\"shouldDisableAddGroup\"\n [attr.data-cy]=\"'groups-add-group-button'\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"plus-circle\"\n ></i>\n {{ 'Add group' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n src=\"/docs/device-management-application/grouping-devices/#grouping-devices\"\n *ngIf=\"moduleConfig.showGroupsContextHelp\"\n></c8y-help>\n\n<div [ngClass]=\"{ drawerOpen: showAddGroup()}\">\n <div class=\"bottom-drawer\">\n <c8y-add-group\n [refresh]=\"refresh\"\n (onCancel)=\"onAddGroupClick()\"\n *ngIf=\"showAddGroup()\"\n ></c8y-add-group>\n </div>\n</div>\n<c8y-sub-assets-grid\n class=\"content-fullpage d-flex d-col border-top border-bottom\"\n [refresh]=\"refresh\"\n [filterable]=\"filterable\"\n [sortable]=\"sortable\"\n [columns]=\"columns\"\n [columnsConfigKey]=\"'sub-assets-grid'\"\n [baseQuery]=\"moduleConfig.baseQuery\"\n></c8y-sub-assets-grid>\n", dependencies: [{ kind: "component", type: i3.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "component", type: i3.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i3.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: i3.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "component", type: AddGroupComponent, selector: "c8y-add-group", inputs: ["currentGroupId", "refresh"], outputs: ["onDeviceQueryStringChange", "onCancel"] }, { kind: "component", type: SubAssetsGridComponent, selector: "c8y-sub-assets-grid", inputs: ["parent-group", "refresh", "title", "emptyStateText", "loadingItemsLabel", "columnsConfigKey", "columns", "pagination", "actionControls", "selectable", "baseQuery", "bulkActionControls", "filterable", "sortable", "displayOptions"], outputs: ["onColumnsChange", "itemsSelect"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
2015
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: GroupsComponent, selector: "c8y-groups-name", ngImport: i0, template: "<c8y-title>\n {{ moduleConfig.name | translate }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-group-open\"\n label=\"{{ moduleConfig.name | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n [placement]=\"'right'\"\n *ngIf=\"moduleConfig.showAddGroupBtn\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Add group' | translate }}\"\n (click)=\"onAddGroupClick()\"\n [disabled]=\"shouldDisableAddGroup\"\n [attr.data-cy]=\"'groups-add-group-button'\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"plus-circle\"\n ></i>\n {{ 'Add group' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n src=\"/docs/device-management-application/grouping-devices/#grouping-devices\"\n *ngIf=\"moduleConfig.showGroupsContextHelp\"\n></c8y-help>\n\n<c8y-add-group\n [refresh]=\"refresh\"\n (onCancel)=\"onAddGroupClick()\"\n *ngIf=\"showAddGroup()\"\n></c8y-add-group>\n<c8y-sub-assets-grid\n class=\"content-fullpage d-flex d-col border-top border-bottom\"\n [refresh]=\"refresh\"\n [filterable]=\"filterable\"\n [sortable]=\"sortable\"\n [columns]=\"columns\"\n [columnsConfigKey]=\"'sub-assets-grid'\"\n [baseQuery]=\"moduleConfig.baseQuery\"\n></c8y-sub-assets-grid>\n", dependencies: [{ kind: "component", type: i3.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "component", type: i3.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i3.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: i3.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "component", type: AddGroupComponent, selector: "c8y-add-group", inputs: ["currentGroupId", "refresh"], outputs: ["onDeviceQueryStringChange", "onCancel"] }, { kind: "component", type: SubAssetsGridComponent, selector: "c8y-sub-assets-grid", inputs: ["parent-group", "refresh", "title", "emptyStateText", "loadingItemsLabel", "columnsConfigKey", "columns", "pagination", "actionControls", "selectable", "baseQuery", "bulkActionControls", "filterable", "sortable", "displayOptions"], outputs: ["onColumnsChange", "itemsSelect"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
1948
2016
  }
1949
2017
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: GroupsComponent, decorators: [{
1950
2018
  type: Component,
1951
- args: [{ selector: 'c8y-groups-name', template: "<c8y-title>\n {{ moduleConfig.name | translate }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-group-open\"\n label=\"{{ moduleConfig.name | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n [placement]=\"'right'\"\n *ngIf=\"moduleConfig.showAddGroupBtn\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Add group' | translate }}\"\n (click)=\"onAddGroupClick()\"\n [disabled]=\"shouldDisableAddGroup\"\n [attr.data-cy]=\"'groups-add-group-button'\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"plus-circle\"\n ></i>\n {{ 'Add group' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n src=\"/docs/device-management-application/grouping-devices/#grouping-devices\"\n *ngIf=\"moduleConfig.showGroupsContextHelp\"\n></c8y-help>\n\n<div [ngClass]=\"{ drawerOpen: showAddGroup()}\">\n <div class=\"bottom-drawer\">\n <c8y-add-group\n [refresh]=\"refresh\"\n (onCancel)=\"onAddGroupClick()\"\n *ngIf=\"showAddGroup()\"\n ></c8y-add-group>\n </div>\n</div>\n<c8y-sub-assets-grid\n class=\"content-fullpage d-flex d-col border-top border-bottom\"\n [refresh]=\"refresh\"\n [filterable]=\"filterable\"\n [sortable]=\"sortable\"\n [columns]=\"columns\"\n [columnsConfigKey]=\"'sub-assets-grid'\"\n [baseQuery]=\"moduleConfig.baseQuery\"\n></c8y-sub-assets-grid>\n" }]
2019
+ args: [{ selector: 'c8y-groups-name', template: "<c8y-title>\n {{ moduleConfig.name | translate }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-group-open\"\n label=\"{{ moduleConfig.name | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n [placement]=\"'right'\"\n *ngIf=\"moduleConfig.showAddGroupBtn\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Add group' | translate }}\"\n (click)=\"onAddGroupClick()\"\n [disabled]=\"shouldDisableAddGroup\"\n [attr.data-cy]=\"'groups-add-group-button'\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"plus-circle\"\n ></i>\n {{ 'Add group' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n src=\"/docs/device-management-application/grouping-devices/#grouping-devices\"\n *ngIf=\"moduleConfig.showGroupsContextHelp\"\n></c8y-help>\n\n<c8y-add-group\n [refresh]=\"refresh\"\n (onCancel)=\"onAddGroupClick()\"\n *ngIf=\"showAddGroup()\"\n></c8y-add-group>\n<c8y-sub-assets-grid\n class=\"content-fullpage d-flex d-col border-top border-bottom\"\n [refresh]=\"refresh\"\n [filterable]=\"filterable\"\n [sortable]=\"sortable\"\n [columns]=\"columns\"\n [columnsConfigKey]=\"'sub-assets-grid'\"\n [baseQuery]=\"moduleConfig.baseQuery\"\n></c8y-sub-assets-grid>\n" }]
1952
2020
  }], ctorParameters: () => [{ type: i3.Permissions }, { type: SubAssetsService }, { type: undefined, decorators: [{
1953
2021
  type: Inject,
1954
2022
  args: [SUB_ASSETS_CONFIG]
@@ -2042,7 +2110,7 @@ class SubAssetsComponent {
2042
2110
  });
2043
2111
  }
2044
2112
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsComponent, deps: [{ token: i1$2.ActivatedRoute }, { token: SubAssetsService }, { token: i3.ContextRouteService }, { token: i3.Permissions }, { token: SUB_ASSETS_CONFIG }, { token: i1$2.Router }], target: i0.ɵɵFactoryTarget.Component }); }
2045
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SubAssetsComponent, selector: "c8y-sub-assets", ngImport: i0, template: "<c8y-title>\n {{ title }}\n</c8y-title>\n\n<c8y-action-bar-item\n [placement]=\"'right'\"\n *ngIf=\"!isSmartGroup && moduleConfig.showAddGroupBtn\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Add group' | translate }}\"\n (click)=\"onAddGroupClick()\"\n [disabled]=\"shouldDisableAddGroup\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"plus-circle\"\n ></i>\n <span translate>Add group</span>\n </button>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'right'\"\n *ngIf=\"!isSmartGroup && moduleConfig.showAssignDeviceBtn\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Assign devices' | translate }}\"\n (click)=\"showAssignDevices = !showAssignDevices\"\n [disabled]=\"shouldDisableAssignDevices\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"plus-circle\"\n ></i>\n <span translate>Assign devices</span>\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n *ngIf=\"isSmartGroup; else assetsHelp\"\n src=\"/docs/device-management-application/grouping-devices/#using-smart-groups\"\n></c8y-help>\n\n<ng-template #assetsHelp>\n <c8y-help src=\"/docs/cockpit/managing-assets/#managing-assets\"></c8y-help>\n</ng-template>\n\n<div\n class=\"card content-fullpage\"\n [ngClass]=\"{\n 'card--grid grid__col--8-4--md grid__row--fit-auto': shouldShowAssetsProperties,\n 'd-flex d-col': !shouldShowAssetsProperties\n }\"\n>\n <c8y-group-info\n class=\"grid__col--fullspan\"\n *ngIf=\"moduleConfig.showDetails\"\n [group]=\"group\"\n (groupChange)=\"groupChange($event)\"\n ></c8y-group-info>\n <c8y-sub-assets-grid\n class=\"d-contents\"\n [refresh]=\"refresh\"\n [parent-group]=\"group\"\n [filterable]=\"filterable\"\n [displayOptions]=\"displayOptions\"\n [columnsConfigKey]=\"'sub-assets-grid'\"\n [baseQuery]=\"moduleConfig.baseQuery\"\n ></c8y-sub-assets-grid>\n <div\n class=\"inner-scroll bg-level-1\"\n *ngIf=\"shouldShowAssetsProperties\"\n >\n <c8y-asset-properties\n class=\"d-contents\"\n [properties]=\"customProperties\"\n [asset]=\"group\"\n (assetChange)=\"groupChange($event)\"\n ></c8y-asset-properties>\n </div>\n</div>\n\n<div [ngClass]=\"{ drawerOpen: showAddGroup() }\">\n <div class=\"bottom-drawer\">\n <c8y-add-group\n [currentGroupId]=\"currentGroupId\"\n [refresh]=\"refresh\"\n (onCancel)=\"onAddGroupClick()\"\n *ngIf=\"showAddGroup()\"\n ></c8y-add-group>\n </div>\n</div>\n\n<div [ngClass]=\"{ drawerOpen: showAssignDevices }\">\n <div class=\"bottom-drawer\">\n <div class=\"d-flex d-col no-align-items fit-h\">\n <c8y-assign-devices\n class=\"d-contents\"\n (onCancel)=\"showAssignDevices = false\"\n [refresh]=\"refresh\"\n [currentGroupId]=\"currentGroupId\"\n (onShowChildDevices)=\"showAssignChildDevices = $event\"\n (selectedDevice)=\"showChildrenForDevice = $event\"\n *ngIf=\"showAssignDevices\"\n ></c8y-assign-devices>\n </div>\n </div>\n</div>\n\n<div [ngClass]=\"{ drawerOpen: showAssignChildDevices }\">\n <div class=\"bottom-drawer m-t-40\">\n <div class=\"d-flex d-col no-align-items fit-h\">\n <c8y-assign-child-devices\n class=\"d-contents\"\n *ngIf=\"showAssignChildDevices\"\n (onCancel)=\"showAssignChildDevices = false\"\n [refresh]=\"refresh\"\n [currentGroupId]=\"currentGroupId\"\n [parentDevice]=\"showChildrenForDevice\"\n ></c8y-assign-child-devices>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "component", type: i3.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: i3.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "component", type: AddGroupComponent, selector: "c8y-add-group", inputs: ["currentGroupId", "refresh"], outputs: ["onDeviceQueryStringChange", "onCancel"] }, { kind: "component", type: SubAssetsGridComponent, selector: "c8y-sub-assets-grid", inputs: ["parent-group", "refresh", "title", "emptyStateText", "loadingItemsLabel", "columnsConfigKey", "columns", "pagination", "actionControls", "selectable", "baseQuery", "bulkActionControls", "filterable", "sortable", "displayOptions"], outputs: ["onColumnsChange", "itemsSelect"] }, { kind: "component", type: GroupInfoComponent, selector: "c8y-group-info", inputs: ["group"], outputs: ["groupChange"] }, { kind: "component", type: AssignDevicesComponent, selector: "c8y-assign-devices", inputs: ["currentGroupId", "refresh"], outputs: ["onCancel", "onShowChildDevices", "selectedDevice"] }, { kind: "component", type: AssignChildDevicesComponent, selector: "c8y-assign-child-devices", inputs: ["currentGroupId", "parentDevice", "refresh"], outputs: ["onCancel"] }, { kind: "component", type: AssetPropertiesComponent, selector: "c8y-asset-properties", inputs: ["asset", "properties"], outputs: ["assetChange"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
2113
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SubAssetsComponent, selector: "c8y-sub-assets", ngImport: i0, template: "<c8y-title>\n {{ title }}\n</c8y-title>\n\n<c8y-action-bar-item\n [placement]=\"'right'\"\n *ngIf=\"!isSmartGroup && moduleConfig.showAddGroupBtn\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Add group' | translate }}\"\n (click)=\"onAddGroupClick()\"\n [disabled]=\"shouldDisableAddGroup\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"plus-circle\"\n ></i>\n <span translate>Add group</span>\n </button>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'right'\"\n *ngIf=\"!isSmartGroup && moduleConfig.showAssignDeviceBtn\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Assign devices' | translate }}\"\n (click)=\"showAssignDevices = !showAssignDevices\"\n [disabled]=\"shouldDisableAssignDevices\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"plus-circle\"\n ></i>\n <span translate>Assign devices</span>\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n *ngIf=\"isSmartGroup; else assetsHelp\"\n src=\"/docs/device-management-application/grouping-devices/#using-smart-groups\"\n></c8y-help>\n\n<ng-template #assetsHelp>\n <c8y-help src=\"/docs/cockpit/managing-assets/#managing-assets\"></c8y-help>\n</ng-template>\n\n<div\n class=\"card content-fullpage\"\n [ngClass]=\"{\n 'card--grid grid__col--8-4--md grid__row--fit-auto': shouldShowAssetsProperties,\n 'd-flex d-col': !shouldShowAssetsProperties\n }\"\n>\n <c8y-group-info\n class=\"grid__col--fullspan\"\n *ngIf=\"moduleConfig.showDetails\"\n [group]=\"group\"\n (groupChange)=\"groupChange($event)\"\n ></c8y-group-info>\n <c8y-sub-assets-grid\n class=\"d-contents\"\n [refresh]=\"refresh\"\n [parent-group]=\"group\"\n [filterable]=\"filterable\"\n [displayOptions]=\"displayOptions\"\n [columnsConfigKey]=\"'sub-assets-grid'\"\n [baseQuery]=\"moduleConfig.baseQuery\"\n ></c8y-sub-assets-grid>\n <div\n class=\"inner-scroll bg-level-1\"\n *ngIf=\"shouldShowAssetsProperties\"\n >\n <c8y-asset-properties\n class=\"d-contents\"\n [properties]=\"customProperties\"\n [asset]=\"group\"\n (assetChange)=\"groupChange($event)\"\n ></c8y-asset-properties>\n </div>\n</div>\n\n<div [ngClass]=\"{ drawerOpen: showAddGroup() }\">\n <div class=\"bottom-drawer\">\n <c8y-add-group\n [currentGroupId]=\"currentGroupId\"\n [refresh]=\"refresh\"\n (onCancel)=\"onAddGroupClick()\"\n *ngIf=\"showAddGroup()\"\n ></c8y-add-group>\n </div>\n</div>\n\n<div [ngClass]=\"{ drawerOpen: showAssignDevices }\">\n <div class=\"bottom-drawer\">\n <div class=\"d-flex d-col no-align-items fit-h\">\n <c8y-assign-devices\n class=\"d-contents\"\n (onCancel)=\"showAssignDevices = false\"\n [refresh]=\"refresh\"\n [currentGroupId]=\"currentGroupId\"\n (onShowChildDevices)=\"showAssignChildDevices = $event\"\n (selectedDevice)=\"showChildrenForDevice = $event\"\n *ngIf=\"showAssignDevices\"\n ></c8y-assign-devices>\n </div>\n </div>\n</div>\n\n<div [ngClass]=\"{ drawerOpen: showAssignChildDevices }\">\n <div class=\"bottom-drawer m-t-40\">\n <div class=\"d-flex d-col no-align-items fit-h\">\n <c8y-assign-child-devices\n class=\"d-contents\"\n *ngIf=\"showAssignChildDevices\"\n (onCancel)=\"showAssignChildDevices = false\"\n [refresh]=\"refresh\"\n [currentGroupId]=\"currentGroupId\"\n [parentDevice]=\"showChildrenForDevice\"\n ></c8y-assign-child-devices>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "component", type: i3.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: i3.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "component", type: AddGroupComponent, selector: "c8y-add-group", inputs: ["currentGroupId", "refresh"], outputs: ["onDeviceQueryStringChange", "onCancel"] }, { kind: "component", type: SubAssetsGridComponent, selector: "c8y-sub-assets-grid", inputs: ["parent-group", "refresh", "title", "emptyStateText", "loadingItemsLabel", "columnsConfigKey", "columns", "pagination", "actionControls", "selectable", "baseQuery", "bulkActionControls", "filterable", "sortable", "displayOptions"], outputs: ["onColumnsChange", "itemsSelect"] }, { kind: "component", type: AssignDevicesComponent, selector: "c8y-assign-devices", inputs: ["currentGroupId", "refresh"], outputs: ["onCancel", "onShowChildDevices", "selectedDevice"] }, { kind: "component", type: AssignChildDevicesComponent, selector: "c8y-assign-child-devices", inputs: ["currentGroupId", "parentDevice", "refresh", "onlySelect"], outputs: ["onCancel", "onSelectedDevices"] }, { kind: "component", type: GroupInfoComponent, selector: "c8y-group-info", inputs: ["group"], outputs: ["groupChange"] }, { kind: "component", type: AssetPropertiesComponent, selector: "c8y-asset-properties", inputs: ["asset", "properties"], outputs: ["assetChange"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
2046
2114
  }
2047
2115
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsComponent, decorators: [{
2048
2116
  type: Component,
@@ -2066,13 +2134,10 @@ class SubAssetsModule {
2066
2134
  }
2067
2135
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2068
2136
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsModule, declarations: [SubAssetsComponent,
2069
- SubAssetsGridComponent,
2070
2137
  GroupsComponent,
2071
2138
  GroupInfoComponent,
2072
2139
  DeleteAssetsModalComponent,
2073
2140
  UnassignModalComponent,
2074
- AssignDevicesComponent,
2075
- AssignChildDevicesComponent,
2076
2141
  AssetPropertiesComponent,
2077
2142
  AssetPropertiesItemComponent,
2078
2143
  AssetLocationComponent], imports: [CoreModule,
@@ -2083,7 +2148,8 @@ class SubAssetsModule {
2083
2148
  TooltipModule,
2084
2149
  FilterMapperModule,
2085
2150
  MapModule,
2086
- AssetTypeCellRendererComponent], exports: [SubAssetsGridComponent, AssetLocationComponent] }); }
2151
+ AssetTypeCellRendererComponent,
2152
+ SubAssetsGridsModule], exports: [SubAssetsGridComponent, AssetLocationComponent] }); }
2087
2153
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsModule, providers: [
2088
2154
  {
2089
2155
  provide: SUB_ASSETS_CONFIG,
@@ -2113,20 +2179,18 @@ class SubAssetsModule {
2113
2179
  TooltipModule,
2114
2180
  FilterMapperModule,
2115
2181
  MapModule,
2116
- AssetTypeCellRendererComponent] }); }
2182
+ AssetTypeCellRendererComponent,
2183
+ SubAssetsGridsModule] }); }
2117
2184
  }
2118
2185
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SubAssetsModule, decorators: [{
2119
2186
  type: NgModule,
2120
2187
  args: [{
2121
2188
  declarations: [
2122
2189
  SubAssetsComponent,
2123
- SubAssetsGridComponent,
2124
2190
  GroupsComponent,
2125
2191
  GroupInfoComponent,
2126
2192
  DeleteAssetsModalComponent,
2127
2193
  UnassignModalComponent,
2128
- AssignDevicesComponent,
2129
- AssignChildDevicesComponent,
2130
2194
  AssetPropertiesComponent,
2131
2195
  AssetPropertiesItemComponent,
2132
2196
  AssetLocationComponent
@@ -2140,7 +2204,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
2140
2204
  TooltipModule,
2141
2205
  FilterMapperModule,
2142
2206
  MapModule,
2143
- AssetTypeCellRendererComponent
2207
+ AssetTypeCellRendererComponent,
2208
+ SubAssetsGridsModule
2144
2209
  ],
2145
2210
  exports: [SubAssetsGridComponent, AssetLocationComponent],
2146
2211
  providers: [