@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
1 lines • 105 kB
Source Map (JSON)
{"version":3,"file":"c8y-ngx-components-repository-configuration.mjs","sources":["../../repository/configuration/device-tab/device-configuration.service.ts","../../repository/configuration/device-tab/text-based-configuration.component.ts","../../repository/configuration/device-tab/text-based-configuration.component.html","../../repository/configuration/device-tab/device-configuration.guard.ts","../../repository/configuration/device-tab/configuration-filter.pipe.ts","../../repository/configuration/device-tab/save-to-repository.component.ts","../../repository/configuration/device-tab/save-to-repository.component.html","../../repository/configuration/device-tab/source-code-preview.component.ts","../../repository/configuration/device-tab/source-code-preview.component.html","../../repository/configuration/device-tab/configuration-preview.component.ts","../../repository/configuration/device-tab/configuration-preview.component.html","../../repository/configuration/device-tab/device-configuration-list.component.ts","../../repository/configuration/device-tab/device-configuration-list.component.html","../../repository/configuration/device-tab/device-configuration.component.ts","../../repository/configuration/device-tab/device-configuration.component.html","../../repository/configuration/device-tab/configuration-repository-device-tab.module.ts","../../repository/configuration/list/configuration-detail.component.ts","../../repository/configuration/list/configuration-detail.component.html","../../repository/configuration/list/configuration-list.component.ts","../../repository/configuration/list/configuration-list.component.html","../../repository/configuration/list/configuration-repository-navigation-factory.ts","../../repository/configuration/list/configuration-repository-list.module.ts","../../repository/configuration/configuration-repository.module.ts","../../repository/configuration/c8y-ngx-components-repository-configuration.ts"],"sourcesContent":["import { EventEmitter, Injectable } from '@angular/core';\nimport { DeviceConfigurationOperation } from '@c8y/ngx-components/repository/shared';\nimport { IManagedObject } from '@c8y/client';\n\n@Injectable()\nexport class DeviceConfigurationService {\n configurationsUpdated = new EventEmitter<any>();\n\n updateConfigurations(repositorySnapsOnly?: boolean) {\n this.configurationsUpdated.emit(repositorySnapsOnly);\n }\n\n hasAnySupportedOperation(\n mo: IManagedObject,\n operation: DeviceConfigurationOperation | DeviceConfigurationOperation[]\n ) {\n const supported = mo.c8y_SupportedOperations as [];\n if (!supported) {\n return false;\n }\n if (!Array.isArray(operation)) {\n operation = [operation];\n }\n return supported.some(supportedOperation => operation.includes(supportedOperation));\n }\n}\n","import { Component, OnInit } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { IManagedObject, IOperation, InventoryService, OperationStatus } from '@c8y/client';\nimport { AlertService, gettext } from '@c8y/ngx-components';\nimport {\n DeviceConfigurationOperation,\n RepositoryService\n} from '@c8y/ngx-components/repository/shared';\nimport { DeviceConfigurationService } from './device-configuration.service';\n\n@Component({\n selector: 'c8y-text-based-configuration',\n templateUrl: './text-based-configuration.component.html',\n standalone: false\n})\nexport class TextBasedConfigurationComponent implements OnInit {\n device: IManagedObject;\n latestOperation: IOperation;\n showTextBasedConfigReload: boolean;\n showTextBasedConfigSave: boolean;\n reloadingConfig = false;\n config: string;\n\n constructor(\n private route: ActivatedRoute,\n private alertService: AlertService,\n private repositoryService: RepositoryService,\n private deviceConfigurationService: DeviceConfigurationService,\n private inventoryService: InventoryService\n ) {}\n\n async ngOnInit() {\n await this.load();\n }\n\n async load() {\n this.device = this.route.snapshot.parent.data.contextData;\n await this.loadDevice();\n await this.loadOperation();\n this.showTextBasedConfigReload = this.deviceConfigurationService.hasAnySupportedOperation(\n this.device,\n [DeviceConfigurationOperation.SEND_CONFIG]\n );\n this.showTextBasedConfigSave = this.deviceConfigurationService.hasAnySupportedOperation(\n this.device,\n [DeviceConfigurationOperation.CONFIG]\n );\n if (this.device.c8y_Configuration && this.device.c8y_Configuration.config) {\n this.config = this.device.c8y_Configuration.config;\n }\n }\n\n async loadOperation() {\n const operation = await this.repositoryService.getLastConfigUpdateOperation(this.device.id);\n if (operation !== null) {\n this.reloadingConfig =\n !!operation.c8y_SendConfiguration &&\n (operation.status === OperationStatus.PENDING ||\n operation.status === OperationStatus.EXECUTING);\n this.repositoryService.observeOperation(operation).subscribe(operationUpdate => {\n if (\n operationUpdate.status === OperationStatus.PENDING ||\n operationUpdate.status === OperationStatus.EXECUTING\n ) {\n this.latestOperation = operationUpdate;\n } else this.latestOperation = null;\n });\n }\n }\n\n get savingConfig() {\n return this.latestOperation\n ? !!this.latestOperation.c8y_Configuration &&\n (this.latestOperation.status === OperationStatus.PENDING ||\n this.latestOperation.status === OperationStatus.EXECUTING)\n : false;\n }\n\n async reloadConfiguration() {\n this.reloadingConfig = true;\n const operationCfg = await this.repositoryService.createTextBasedConfigurationReloadOperation(\n this.device\n );\n try {\n this.repositoryService.createObservedOperation(operationCfg).subscribe(\n operationUpdate => this.onOperationReloadSuccess(operationUpdate),\n operationUpdate => this.onOperationReloadError(operationUpdate),\n () => this.onOperationReloadComplete()\n );\n } catch (ex) {\n this.alertService.addServerFailure(ex);\n }\n }\n\n async updateConfiguration(config) {\n const operationCfg = await this.repositoryService.createTextBasedConfigurationUpdateOperation(\n this.device,\n config\n );\n try {\n this.repositoryService.createObservedOperation(operationCfg).subscribe(\n operationUpdate => this.onOperationUpdateSuccess(operationUpdate),\n operationUpdate => this.onOperationUpdateError(operationUpdate),\n () => this.onOperationUpdateComplete()\n );\n } catch (ex) {\n this.alertService.addServerFailure(ex);\n }\n }\n\n private onOperationReloadSuccess(operationUpdate) {\n this.latestOperation = operationUpdate;\n if (operationUpdate.status === OperationStatus.PENDING) {\n this.alertService.success(gettext('Configuration will be reloaded.'));\n }\n }\n\n private onOperationReloadError(operationUpdate) {\n this.latestOperation = operationUpdate;\n this.reloadingConfig = false;\n }\n\n private async onOperationReloadComplete() {\n await this.loadDevice();\n this.config = this.device.c8y_Configuration.config;\n this.reloadingConfig = false;\n }\n\n private onOperationUpdateSuccess(operationUpdate) {\n this.latestOperation = operationUpdate;\n if (operationUpdate.status === OperationStatus.PENDING) {\n this.alertService.success(gettext('Configuration will be updated.'));\n }\n }\n\n private onOperationUpdateError(operationUpdate) {\n this.latestOperation = operationUpdate;\n }\n\n private onOperationUpdateComplete() {\n this.device.c8y_Configuration.config = this.config;\n }\n\n private async loadDevice() {\n this.device = (\n await this.inventoryService.detail(this.device.id, {\n withChildren: false\n })\n ).data;\n }\n}\n","<div class=\"d-flex d-col fit-h\">\n <fieldset class=\"card-block bg-level-1 fit-w\">\n <div class=\"content-flex-50\">\n <div class=\"m-l-auto d-flex\">\n <button\n class=\"btn btn-default btn-sm a-s-center m-t-8 m-b-8\"\n title=\"{{ 'Get configuration from device' | translate }}\"\n type=\"button\"\n *ngIf=\"showTextBasedConfigReload\"\n (click)=\"reloadConfiguration()\"\n [disabled]=\"reloadingConfig || savingConfig\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"refresh\"\n *ngIf=\"reloadingConfig\"\n [ngClass]=\"{ 'icon-spin': reloadingConfig }\"\n ></i>\n <i\n class=\"m-r-4\"\n c8yIcon=\"download\"\n *ngIf=\"!reloadingConfig\"\n ></i>\n\n {{ 'Get configuration from device' | translate }}\n </button>\n </div>\n </div>\n </fieldset>\n <div class=\"flex-grow\">\n <textarea\n class=\"form-control fit-h p-r-16 p-l-16\"\n [attr.aria-label]=\"'Operations' | translate\"\n [(ngModel)]=\"config\"\n [disabled]=\"reloadingConfig || savingConfig\"\n c8y-spellcheck=\"false\"\n ></textarea>\n </div>\n <c8y-operation-details\n class=\"bg-level-2 p-0\"\n *ngIf=\"latestOperation !== undefined\"\n [operation]=\"latestOperation\"\n ></c8y-operation-details>\n <div\n class=\"card-footer fit-w separator\"\n *ngIf=\"showTextBasedConfigSave\"\n >\n <button\n class=\"btn btn-primary\"\n id=\"send-config-btn\"\n type=\"button\"\n (click)=\"updateConfiguration(config)\"\n [disabled]=\"reloadingConfig || savingConfig || !config\"\n [ngClass]=\"{ 'btn-pending': savingConfig }\"\n >\n <span\n title=\"{{ 'Send' | translate }}\"\n *ngIf=\"!savingConfig\"\n >\n {{ 'Send configuration to device' | translate }}\n </span>\n <span\n title=\"{{ 'Sending…' | translate }}\"\n *ngIf=\"savingConfig\"\n >\n {{ 'Sending…' | translate }}\n </span>\n </button>\n </div>\n</div>\n","import { Injectable } from '@angular/core';\nimport { ActivatedRouteSnapshot } from '@angular/router';\nimport { DeviceConfigurationOperation } from '@c8y/ngx-components/repository/shared';\nimport { has } from 'lodash-es';\nimport { DeviceConfigurationService } from './device-configuration.service';\n\n@Injectable()\nexport class DeviceConfigurationGuard {\n constructor(private deviceConfigurationService: DeviceConfigurationService) {}\n\n canActivate(route: ActivatedRouteSnapshot) {\n const contextData = route.data.contextData || route.parent.data.contextData;\n if (!contextData) {\n return false;\n }\n return (\n (contextData.c8y_SupportedConfigurations &&\n contextData.c8y_SupportedConfigurations.length > 0) ||\n this.deviceConfigurationService.hasAnySupportedOperation(contextData, [\n DeviceConfigurationOperation.DOWNLOAD_CONFIG,\n DeviceConfigurationOperation.UPLOAD_CONFIG,\n DeviceConfigurationOperation.CONFIG,\n DeviceConfigurationOperation.SEND_CONFIG\n ]) ||\n has(contextData, 'c8y_Configuration')\n );\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { SupportedConfigurationItem } from '@c8y/ngx-components/repository/shared';\n\n@Pipe({\n name: 'configurationFilterPipe',\n standalone: false\n})\nexport class ConfigurationFilterPipe implements PipeTransform {\n transform(items: any[], filterTerm: string): any {\n return filterTerm.trim().length === 0\n ? items\n : items.filter(\n (item: SupportedConfigurationItem) =>\n this.filterContainString(item.name, filterTerm) ||\n this.filterContainString(item.deviceType, filterTerm)\n );\n }\n\n private filterContainString(name: string, filterTerm: string) {\n const term = filterTerm.toLowerCase().trim();\n return name && name.toLowerCase().indexOf(term) > -1;\n }\n}\n","import { Component } from '@angular/core';\nimport { AlertService, gettext } from '@c8y/ngx-components';\nimport {\n ConfigurationSnapshot,\n RepositoryService,\n RepositoryType\n} from '@c8y/ngx-components/repository/shared';\nimport { BsModalRef } from 'ngx-bootstrap/modal';\n\n@Component({\n selector: 'c8y-save-config-to-configuration-repository',\n templateUrl: 'save-to-repository.component.html',\n standalone: false\n})\nexport class SaveToRepositoryComponent {\n configSnapshot: ConfigurationSnapshot;\n result: Promise<any> = new Promise((resolve, reject) => {\n this._save = resolve;\n this._cancel = reject;\n });\n private _save;\n private _cancel;\n\n constructor(\n private modal: BsModalRef,\n private alertService: AlertService,\n private repositoryService: RepositoryService\n ) {}\n\n async save() {\n {\n try {\n const configSnapshotData = {\n selected: {\n configurationType: this.configSnapshot.configurationType\n },\n version: this.configSnapshot.name,\n deviceType: this.configSnapshot.deviceType,\n description: this.configSnapshot.description,\n binary: {\n file: new File([this.configSnapshot.binary], this.configSnapshot.name)\n }\n };\n await this.repositoryService.create(configSnapshotData, RepositoryType.CONFIGURATION);\n this.alertService.success(gettext('Configuration saved.'));\n this._save();\n } catch (ex) {\n this.alertService.addServerFailure(ex);\n }\n }\n }\n\n close() {\n this._cancel();\n this.modal.hide();\n }\n}\n","<div class=\"modal-header dialog-header\">\n <i c8yIcon=\"gears\"></i>\n <h4 id=\"modal-title\" translate>\n Save configuration\n </h4>\n</div>\n<div class=\"modal-body\" id=\"modal-body\">\n <form #saveConfigurationSnapshot=\"ngForm\" class=\"p-t-24\">\n <c8y-form-group>\n <label translate for=\"name\">Name</label>\n <input\n id=\"name\"\n type=\"text\"\n class=\"form-control\"\n autocomplete=\"off\"\n name=\"name\"\n [(ngModel)]=\"configSnapshot.name\"\n required\n />\n </c8y-form-group>\n <c8y-form-group>\n <label translate for=\"deviceType\">Device type</label>\n <input\n id=\"deviceType\"\n class=\"form-control\"\n rows=\"6\"\n name=\"deviceType\"\n [(ngModel)]=\"configSnapshot.deviceType\"\n />\n </c8y-form-group>\n <c8y-form-group>\n <label translate for=\"description\">Description</label>\n <input\n type=\"text\"\n id=\"description\"\n class=\"form-control\"\n maxlength=\"254\"\n autocomplete=\"off\"\n name=\"description\"\n [(ngModel)]=\"configSnapshot.description\"\n />\n </c8y-form-group>\n <c8y-form-group>\n <label translate for=\"configurationType\">Configuration type</label>\n <input\n id=\"configurationType\"\n class=\"form-control\"\n rows=\"6\"\n name=\"configurationType\"\n [(ngModel)]=\"configSnapshot.configurationType\"\n />\n </c8y-form-group>\n </form>\n</div>\n<div class=\"modal-footer\">\n <button title=\"{{ 'Cancel' | translate }}\" class=\"btn btn-default\" (click)=\"close()\" translate>\n Cancel\n </button>\n\n <button\n title=\"{{ 'Save configuration to repository' | translate }}\"\n class=\"btn btn-primary\"\n (click)=\"save()\"\n [disabled]=\"saveConfigurationSnapshot.form.invalid\"\n translate\n >\n Save\n </button>\n</div>\n","import { Component, Input } from '@angular/core';\n\n@Component({\n selector: 'c8y-source-code-preview',\n templateUrl: './source-code-preview.component.html',\n standalone: false\n})\nexport class SourceCodePreviewComponent {\n @Input() isDisabled: boolean;\n @Input() text: string;\n}\n","<textarea\n [disabled]=\"isDisabled\"\n class=\"text-monospace form-control no-resize flex-grow\"\n rows=\"4\"\n >{{ text }}</textarea\n>\n","import { Component, Input, OnDestroy, OnInit } from '@angular/core';\nimport {\n IManagedObject,\n IOperation,\n OperationService,\n OperationStatus,\n UserService\n} from '@c8y/client';\nimport {\n AlertService,\n AppStateService,\n OperationRealtimeService,\n Permissions\n} from '@c8y/ngx-components';\nimport {\n ConfigurationSnapshot,\n DeviceConfigurationOperation,\n RepositoryService\n} from '@c8y/ngx-components/repository/shared';\nimport { saveAs } from 'file-saver';\nimport { cloneDeep } from 'lodash-es';\nimport { BsModalService } from 'ngx-bootstrap/modal';\nimport { Subscription } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport { DeviceConfigurationService } from './device-configuration.service';\nimport { SaveToRepositoryComponent } from './save-to-repository.component';\n\n@Component({\n selector: 'c8y-device-configuration-preview',\n templateUrl: './configuration-preview.component.html',\n standalone: false\n})\nexport class ConfigurationPreviewComponent implements OnInit, OnDestroy {\n @Input() device: IManagedObject;\n\n @Input() set configurationType(type: string) {\n this._configurationType = type;\n this.setOperation(type);\n }\n\n get configurationType(): string {\n return this._configurationType;\n }\n\n @Input() configSnapshot: ConfigurationSnapshot;\n @Input() canSaveSnapshot: boolean;\n @Input() actionButtonText: string;\n @Input() actionButtonIcon: string;\n @Input() isLegacy = false;\n @Input() operationToTrigger:\n | DeviceConfigurationOperation.UPLOAD_CONFIG\n | DeviceConfigurationOperation.DOWNLOAD_CONFIG;\n\n operation: IOperation;\n canCallAction = true;\n deviceConfigurationOperation = DeviceConfigurationOperation;\n private _configurationType: string;\n private operationsSubscription: Subscription;\n\n constructor(\n private deviceConfigurationService: DeviceConfigurationService,\n private operationRealtime: OperationRealtimeService,\n private bsModal: BsModalService,\n private user: UserService,\n private appState: AppStateService,\n private repositoryService: RepositoryService,\n private operationService: OperationService,\n private alertService: AlertService\n ) {}\n\n async ngOnInit() {\n this.setCanCallAction();\n this.setOperation(this._configurationType);\n this.operationsSubscription = this.operationRealtime\n .onAll$(this.device.id)\n .pipe(map(({ data }) => data as IOperation))\n .subscribe(operation => {\n this.updatePreview(operation);\n });\n }\n\n async setOperation(configType): Promise<void> {\n const operationList = await this.repositoryService.getConfigFileOperationList(\n this.device.id,\n this.operationToTrigger\n );\n\n const operation = this.isLegacy\n ? operationList.find(op => op[this.operationToTrigger] && !op[this.operationToTrigger].type)\n : operationList.find(op => op[this.operationToTrigger].type === configType);\n\n this.operation =\n operation && operation.status !== OperationStatus.SUCCESSFUL ? operation : undefined;\n }\n\n setCanCallAction(): void {\n this.canCallAction = this.deviceConfigurationService.hasAnySupportedOperation(\n this.device,\n this.operationToTrigger\n );\n }\n\n async createDeviceOperation() {\n let operationCfg;\n if (this.operationToTrigger === DeviceConfigurationOperation.DOWNLOAD_CONFIG) {\n operationCfg = this.repositoryService.getDownloadConfigurationFileOperation(\n this.device,\n this._configurationType,\n this.configSnapshot,\n this.isLegacy\n );\n }\n if (this.operationToTrigger === DeviceConfigurationOperation.UPLOAD_CONFIG) {\n operationCfg = this.repositoryService.getUploadConfigurationFileOperation(\n this.device,\n this._configurationType,\n this.isLegacy\n );\n }\n try {\n this.operation = (await this.operationService.create(operationCfg)).data;\n } catch (ex) {\n this.alertService.addServerFailure(ex);\n }\n }\n\n showOperation(): boolean {\n if (this.operationToTrigger === DeviceConfigurationOperation.DOWNLOAD_CONFIG) {\n return !!this.operation;\n }\n return (\n this.operation &&\n [OperationStatus.PENDING, OperationStatus.EXECUTING].includes(this.operation.status)\n );\n }\n\n showBinary(): boolean {\n if (this.operationToTrigger === DeviceConfigurationOperation.DOWNLOAD_CONFIG) {\n return true;\n }\n return !this.showOperation();\n }\n\n isCreateOperationDisabled(): boolean {\n return (\n this.operation &&\n [OperationStatus.PENDING, OperationStatus.EXECUTING].includes(this.operation.status)\n );\n }\n\n updatePreview(operation: IOperation) {\n if (\n operation &&\n operation[this.operationToTrigger] &&\n (this.isLegacy ||\n (operation[this.operationToTrigger].type &&\n operation[this.operationToTrigger].type === this.configurationType))\n ) {\n this.operation = operation;\n this.updateSnapshotsOnConfigUpload(operation);\n }\n }\n\n download() {\n const blob = new Blob([this.configSnapshot.binary], { type: this.configSnapshot.binaryType });\n let fileName = this.configSnapshot.name;\n switch (this.configSnapshot.binaryType) {\n case 'text/csv':\n case 'application/csv':\n fileName = fileName.concat('.csv');\n break;\n case 'text/yaml':\n case 'application/x-yaml':\n fileName = fileName.concat('.yaml');\n break;\n case 'application/json':\n fileName = fileName.concat('.json');\n break;\n }\n saveAs(blob, fileName);\n }\n\n async saveToRepository() {\n const initialState = {\n configSnapshot: cloneDeep(this.configSnapshot)\n };\n const modal = this.bsModal.show(SaveToRepositoryComponent, {\n class: 'modal-sm',\n ariaDescribedby: 'modal-body',\n ariaLabelledBy: 'modal-title',\n initialState,\n ignoreBackdropClick: true\n }).content as SaveToRepositoryComponent;\n try {\n await modal.result;\n this.deviceConfigurationService.updateConfigurations(true);\n modal.close();\n } catch (ex) {\n // do nothing\n }\n }\n\n hasPermission(): boolean {\n return (\n this.user.hasAnyRole(this.appState.currentUser.value, [\n Permissions.ROLE_INVENTORY_ADMIN,\n Permissions.ROLE_INVENTORY_CREATE\n ]) ||\n (this.user.hasAnyRole(this.appState.currentUser.value, [\n Permissions.ROLE_MANAGED_OBJECT_ADMIN,\n Permissions.ROLE_MANAGED_OBJECT_CREATE\n ]) &&\n this.user.hasAnyRole(this.appState.currentUser.value, [\n Permissions.ROLE_BINARY_ADMIN,\n Permissions.ROLE_BINARY_CREATE\n ]))\n );\n }\n\n ngOnDestroy() {\n if (this.operationsSubscription) {\n this.operationsSubscription.unsubscribe();\n }\n }\n\n private async updateSnapshotsOnConfigUpload(operation) {\n if (\n operation[DeviceConfigurationOperation.UPLOAD_CONFIG] &&\n operation.status === OperationStatus.SUCCESSFUL\n ) {\n this.deviceConfigurationService.updateConfigurations();\n }\n }\n}\n","<div class=\"content-flex-55 p-b-16\">\n <div class=\"col-7 p-t-4\">\n <p>\n <span class=\"text-label-small text-uppercase m-r-4\" translate>Configuration</span>\n <span *ngIf=\"configSnapshot?.name; else emptyText\">\n <strong>{{ configSnapshot.name }}</strong>\n </span>\n <ng-template #emptyText>---</ng-template>\n </p>\n <p>\n <span class=\"text-label-small text-uppercase m-r-4\" translate>Last updated</span>\n <small *ngIf=\"configSnapshot?.time; else emptyDate\">\n {{ configSnapshot.time | c8yDate }}\n </small>\n <ng-template #emptyDate>---</ng-template>\n </p>\n </div>\n <div class=\"col-5\">\n <button\n id=\"action-btn\"\n class=\"btn btn-default btn-sm pull-right\"\n type=\"button\"\n title=\"{{ actionButtonText | translate }}\"\n (click)=\"createDeviceOperation()\"\n [disabled]=\"isCreateOperationDisabled()\"\n *ngIf=\"canCallAction\"\n >\n <i [c8yIcon]=\"actionButtonIcon\"></i>\n {{ actionButtonText | translate }}\n </button>\n </div>\n</div>\n<div class=\"c8y-empty-state text-left\" *ngIf=\"!configSnapshot?.binary && showBinary()\">\n <h1 [c8yIcon]=\"'file-image-o'\"></h1>\n <p>\n <strong translate>No preview available.</strong>\n <br />\n <small *ngIf=\"configSnapshot?.binary !== ''; else emptyFile\" translate>\n The file is not available.\n </small>\n <ng-template #emptyFile>\n <small translate>The file is empty.</small>\n </ng-template>\n </p>\n</div>\n<div *ngIf=\"configSnapshot?.binary && showBinary()\" class=\"flex-grow d-flex d-col\">\n <c8y-source-code-preview\n [text]=\"configSnapshot.binary\"\n [isDisabled]=\"true\"\n class=\"d-contents\"\n ></c8y-source-code-preview>\n <div *ngIf=\"canSaveSnapshot\" class=\"p-t-16\">\n <button\n title=\"{{ 'Download' | translate }}\"\n type=\"button\"\n class=\"btn btn-primary btn-sm pull-right m-l-8\"\n (click)=\"download()\"\n >\n {{ 'Download' | translate }}\n </button>\n <button\n title=\"{{ 'Save to repository' | translate }}\"\n *ngIf=\"hasPermission()\"\n type=\"button\"\n class=\"btn btn-default btn-sm pull-right\"\n (click)=\"saveToRepository()\"\n >\n {{ 'Save to repository' | translate }}\n </button>\n </div>\n</div>\n<div *ngIf=\"showOperation()\">\n <c8y-operation-details [operation]=\"operation\"></c8y-operation-details>\n</div>\n","import { Component, EventEmitter, Input, Output } from '@angular/core';\nimport {\n DeviceConfigurationListEmptyState,\n SupportedConfigurationItem\n} from '@c8y/ngx-components/repository/shared';\n\n@Component({\n selector: 'c8y-device-configuration-list',\n templateUrl: './device-configuration-list.component.html',\n standalone: false\n})\nexport class DeviceConfigurationListComponent {\n @Input() items: SupportedConfigurationItem[];\n @Input() itemIcon: string;\n @Input() emptyState: DeviceConfigurationListEmptyState;\n @Input() isFilterEnabled: boolean;\n @Output() configSelected = new EventEmitter<string>();\n selectedConfig;\n filterTerm = '';\n\n showConfigurationTypePreview(config): void {\n this.selectedConfig = config;\n this.configSelected.emit(config);\n }\n\n updatePipe(filterTerm: string) {\n this.filterTerm = filterTerm;\n }\n}\n","<div class=\"p-l-16 m-b-8\" *ngIf=\"isFilterEnabled\">\n <c8y-filter [icon]=\"'search'\" (onSearch)=\"updatePipe($event)\"></c8y-filter>\n</div>\n\n<!-- EMPTY STATE -->\n<div class=\"c8y-empty-state text-left\" *ngIf=\"items?.length === 0\">\n <h1 [c8yIcon]=\"emptyState.icon\"></h1>\n <p>\n <strong>{{ emptyState.title | translate }}</strong>\n <br />\n <small>{{ emptyState.text | translate }}</small>\n </p>\n</div>\n\n<!-- CONFIGURATIONS AVAILABLE -->\n<div class=\"c8y-nav-stacked\">\n <button\n type=\"button\"\n class=\"c8y-stacked-item d-flex\"\n [class.active]=\"config === selectedConfig\"\n *ngFor=\"let config of items | configurationFilterPipe: filterTerm\"\n (click)=\"showConfigurationTypePreview(config)\"\n >\n <div class=\"list-item-icon\">\n <i [c8yIcon]=\"itemIcon\"></i>\n </div>\n <div class=\"list-item-body text-truncate\">\n <div class=\"d-flex\">\n <span class=\"text-truncate\" title=\"{{ config.name }}\">{{ config.name }}</span>\n <span class=\"text-label-small m-l-auto m-t-auto m-b-auto\">{{ config.deviceType }}</span>\n </div>\n </div>\n </button>\n</div>\n\n<!-- for Carlos: config.configurationType to differentiate whether a config matches configuration type. -->\n","import { Component, OnInit, ViewChild } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { IManagedObject, Realtime } from '@c8y/client';\nimport { gettext } from '@c8y/ngx-components';\nimport {\n ConfigurationSnapshot,\n DeviceConfigurationListEmptyState,\n DeviceConfigurationOperation,\n RepositoryService,\n SupportedConfigurationItem\n} from '@c8y/ngx-components/repository/shared';\nimport { has } from 'lodash-es';\nimport { DeviceConfigurationService } from './device-configuration.service';\nimport { TextBasedConfigurationComponent } from './text-based-configuration.component';\n\n@Component({\n selector: 'c8y-device-configuration',\n templateUrl: './device-configuration.component.html',\n standalone: false\n})\nexport class DeviceConfigurationComponent implements OnInit {\n supportedConfigurations: SupportedConfigurationItem[] = [];\n showBinaryBasedConfig = false;\n configurationType: string;\n configSnapshot: Partial<ConfigurationSnapshot> = {};\n repositorySnapshots: IManagedObject[];\n repositorySnapshot: ConfigurationSnapshot;\n repositorySnapshotsEmptyState: DeviceConfigurationListEmptyState;\n device: IManagedObject;\n isLegacy: boolean;\n showTextBasedConfig: boolean;\n reloading = false;\n\n @ViewChild(TextBasedConfigurationComponent)\n textBasedConfigurationComponent: TextBasedConfigurationComponent;\n\n constructor(\n private route: ActivatedRoute,\n private deviceConfigurationService: DeviceConfigurationService,\n private realtime: Realtime,\n private repositoryService: RepositoryService\n ) {\n this.deviceConfigurationService.configurationsUpdated.subscribe(repositorySnapsOnly => {\n this.updateSnapshots(repositorySnapsOnly);\n });\n }\n\n ngOnInit() {\n this.device = this.route.snapshot.parent.data.contextData;\n if (this.device.c8y_SupportedConfigurations) {\n this.supportedConfigurations = this.device.c8y_SupportedConfigurations.map(item => ({\n name: item\n }));\n }\n\n if (\n this.deviceConfigurationService.hasAnySupportedOperation(this.device, [\n DeviceConfigurationOperation.DOWNLOAD_CONFIG,\n DeviceConfigurationOperation.UPLOAD_CONFIG\n ])\n ) {\n this.supportedConfigurations.push({\n name: gettext('Legacy configuration snapshot'),\n isLegacy: true\n });\n }\n if (this.supportedConfigurations.length > 0) {\n this.showBinaryBasedConfig = true;\n }\n\n this.repositorySnapshotsEmptyState = {\n icon: 'gears',\n title: gettext('No configurations available.'),\n text: gettext('Add configuration to configuration repository')\n };\n\n this.showTextBasedConfig =\n this.deviceConfigurationService.hasAnySupportedOperation(this.device, [\n DeviceConfigurationOperation.CONFIG,\n DeviceConfigurationOperation.SEND_CONFIG\n ]) || has(this.device, 'c8y_Configuration');\n }\n\n async onConfigTypeSelected(config) {\n this.configurationType = config.name;\n this.isLegacy = config.isLegacy;\n this.updateSnapshots();\n }\n\n async onRepositoryConfigSelected(config) {\n this.repositorySnapshot = {\n id: config.id,\n time: config.creationTime,\n name: config.name,\n binaryUrl: config.url,\n deviceType: config.deviceType,\n configurationType: config.configurationType\n };\n if (config.url) {\n try {\n const binary = await this.repositoryService.getBinaryFile(config.url, {\n allowExternal: false\n });\n if (binary) {\n this.repositorySnapshot.binary = await (binary as any).text();\n }\n } catch (ex) {\n // do nothing\n }\n }\n }\n\n async updateSnapshots(repositorySnapsOnly?: boolean) {\n this.reloading = true;\n this.repositorySnapshot = undefined;\n this.repositorySnapshots = await this.repositoryService.getSnapshotsFromRepository(\n this.device,\n this.configurationType\n );\n if (!repositorySnapsOnly) {\n this.configSnapshot = this.isLegacy\n ? await this.repositoryService.getLegacyConfigSnapshot(this.device)\n : await this.repositoryService.getConfigSnapshot(this.device, this.configurationType);\n }\n if (this.showTextBasedConfig) {\n await this.textBasedConfigurationComponent.load();\n }\n this.reloading = false;\n }\n}\n","<c8y-action-bar-item [placement]=\"'right'\">\n <button class=\"btn btn-link\" title=\"{{ 'Reload' | translate }}\" (click)=\"updateSnapshots()\">\n <i c8yIcon=\"refresh\" [ngClass]=\"{ 'icon-spin': reloading }\"></i>\n {{ 'Reload' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<div class=\"card content-fullpage card-has-tabs\">\n <tabset>\n <div class=\"card-header separator\" *ngIf=\"showBinaryBasedConfig && !showTextBasedConfig\">\n <div class=\"card-title\">{{ 'Configurations' | translate }}</div>\n </div>\n <div class=\"card-header separator\" *ngIf=\"!showBinaryBasedConfig && showTextBasedConfig\">\n <div class=\"card-title\">{{ 'Text-based configuration' | translate }}</div>\n </div>\n <tab heading=\"{{ 'Configurations' | translate }}\" *ngIf=\"showBinaryBasedConfig\">\n <div class=\"card--grid card grid__col--4-8--md grid__row--6-6--md m-b-0\">\n <!-- DEVICE SUPPORTED CONFIGURATIONS -->\n <div class=\"card--grid__inner-scroll bg-level-1\">\n <div class=\"p-l-16 p-r-16\">\n <h5 class=\"legend form-block\">\n <span translate>Device-supported configurations</span>\n </h5>\n </div>\n <c8y-device-configuration-list\n [items]=\"supportedConfigurations\"\n [itemIcon]=\"'gears'\"\n (configSelected)=\"onConfigTypeSelected($event)\"\n ></c8y-device-configuration-list>\n </div>\n\n <!-- CONFIGURATION PREVIEW -->\n <div class=\"card--grid__inner-scroll d-flex d-col flex-grow\">\n <div class=\"card-block d-flex d-col flex-grow\">\n <h5 class=\"legend form-block\"><span translate>Preview</span></h5>\n\n <!-- EMPTY STATE -->\n\n <c8y-ui-empty-state\n *ngIf=\"!configurationType\"\n [icon]=\"'file-text'\"\n [title]=\"'No configuration selected.' | translate\"\n [subtitle]=\"'Select a configuration to preview.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n\n <!-- PREVIEW AVAILABLE STATE -->\n <c8y-device-configuration-preview\n *ngIf=\"configurationType\"\n [device]=\"device\"\n [configurationType]=\"configurationType\"\n [configSnapshot]=\"configSnapshot\"\n [canSaveSnapshot]=\"true\"\n [operationToTrigger]=\"'c8y_UploadConfigFile'\"\n [actionButtonText]=\"'Get snapshot from device' | translate\"\n [actionButtonIcon]=\"'download'\"\n [isLegacy]=\"isLegacy\"\n class=\"d-flex d-col flex-grow\"\n ></c8y-device-configuration-preview>\n </div>\n </div>\n\n <!-- AVAILABLE SUPPORTED CONFIGURATIONS -->\n <div class=\"card--grid__inner-scroll bg-level-1\">\n <div class=\"p-l-16 p-r-16\">\n <h5 class=\"legend form-block\" translate>Available supported configurations</h5>\n </div>\n\n <!-- EMPTY STATE -->\n\n <c8y-ui-empty-state\n *ngIf=\"!configurationType\"\n [icon]=\"'gears'\"\n [title]=\"'No selection.' | translate\"\n [subtitle]=\"\n 'Select a configuration from the device-supported configuration list.' | translate\n \"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n\n <div class=\"p-r-16\" *ngIf=\"configurationType\">\n <c8y-device-configuration-list\n [items]=\"repositorySnapshots\"\n [itemIcon]=\"'file-text'\"\n [emptyState]=\"repositorySnapshotsEmptyState\"\n [isFilterEnabled]=\"true\"\n (configSelected)=\"onRepositoryConfigSelected($event)\"\n ></c8y-device-configuration-list>\n </div>\n </div>\n\n <!-- CONFIGURATION PREVIEW -->\n <div class=\"card--grid__inner-scroll d-flex d-col flex-grow\">\n <div class=\"card-block flex-grow d-flex d-col\">\n <h5 class=\"legend form-block\" translate>Preview</h5>\n\n <!-- EMPTY STATE -->\n <c8y-ui-empty-state\n *ngIf=\"!repositorySnapshot\"\n [icon]=\"'file-text'\"\n [title]=\"'No configuration selected.' | translate\"\n [subtitle]=\"'Select a configuration to preview.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n\n <!-- CONFIGURATION SELECTED STATE -->\n <c8y-device-configuration-preview\n *ngIf=\"repositorySnapshot\"\n [device]=\"device\"\n [configurationType]=\"configurationType\"\n [configSnapshot]=\"repositorySnapshot\"\n [operationToTrigger]=\"'c8y_DownloadConfigFile'\"\n [actionButtonText]=\"'Send configuration to device' | translate\"\n [actionButtonIcon]=\"'upload'\"\n [isLegacy]=\"isLegacy\"\n class=\"d-flex d-col flex-grow\"\n ></c8y-device-configuration-preview>\n </div>\n </div>\n </div>\n </tab>\n <tab heading=\"{{ 'Text-based configuration' | translate }}\" *ngIf=\"showTextBasedConfig\">\n <c8y-text-based-configuration></c8y-text-based-configuration>\n </tab>\n </tabset>\n</div>\n","import { ModuleWithProviders, NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { CoreModule, ViewContext, gettext, hookRoute } from '@c8y/ngx-components';\nimport { SharedRepositoryModule } from '@c8y/ngx-components/repository/shared';\nimport { DeviceConfigurationGuard } from './device-configuration.guard';\nimport { DeviceConfigurationComponent } from './device-configuration.component';\nimport { ConfigurationPreviewComponent } from './configuration-preview.component';\nimport { ConfigurationFilterPipe } from './configuration-filter.pipe';\nimport { DeviceConfigurationService } from './device-configuration.service';\nimport { DeviceConfigurationListComponent } from './device-configuration-list.component';\nimport { TextBasedConfigurationComponent } from './text-based-configuration.component';\nimport { SaveToRepositoryComponent } from './save-to-repository.component';\nimport { SourceCodePreviewComponent } from './source-code-preview.component';\nimport { TabsModule } from 'ngx-bootstrap/tabs';\n\nimport { OperationDetailsModule } from '@c8y/ngx-components/operations/operation-details';\n\n@NgModule({\n imports: [\n CommonModule,\n CoreModule,\n SharedRepositoryModule,\n OperationDetailsModule,\n TabsModule.forRoot()\n ],\n declarations: [\n DeviceConfigurationComponent,\n DeviceConfigurationListComponent,\n ConfigurationPreviewComponent,\n ConfigurationFilterPipe,\n SaveToRepositoryComponent,\n SourceCodePreviewComponent,\n TextBasedConfigurationComponent\n ],\n providers: [DeviceConfigurationService]\n})\nexport class ConfigurationRepositoryDeviceTabModule {\n static forRoot(): ModuleWithProviders<ConfigurationRepositoryDeviceTabModule> {\n return {\n ngModule: ConfigurationRepositoryDeviceTabModule,\n providers: [\n DeviceConfigurationGuard,\n hookRoute({\n context: ViewContext.Device,\n path: 'device-configuration',\n component: DeviceConfigurationComponent,\n label: gettext('Configuration'),\n icon: 'gears',\n priority: 600,\n canActivate: [DeviceConfigurationGuard]\n })\n ]\n };\n }\n}\n","import { Component, ViewChild } from '@angular/core';\nimport { NgForm } from '@angular/forms';\nimport { IManagedObject } from '@c8y/client';\nimport {\n AlertService,\n ForOfFilterPipe,\n gettext,\n PickedFiles,\n ValidationPattern\n} from '@c8y/ngx-components';\nimport {\n ModalModel,\n RepositoryService,\n RepositoryType\n} from '@c8y/ngx-components/repository/shared';\nimport { isUndefined, uniqBy } from 'lodash-es';\nimport { BsModalRef } from 'ngx-bootstrap/modal';\nimport { pipe } from 'rxjs';\nimport { map } from 'rxjs/operators';\n\n@Component({\n selector: 'c8y-configuration-detail',\n templateUrl: './configuration-detail.component.html',\n standalone: false\n})\nexport class ConfigurationDetailComponent implements ModalModel {\n @ViewChild('configurationForm', { static: true }) configurationForm: NgForm;\n version: string;\n description: string;\n binary: { file?: File; url?: string } = {\n file: undefined,\n url: undefined\n };\n deviceType: string;\n configurationTypeMO: Partial<IManagedObject>;\n\n pattern = '';\n filterPipe: ForOfFilterPipe;\n configs;\n mo: Partial<IManagedObject> = {};\n saving = false;\n uploadChoice: 'uploadBinary' | 'uploadUrl' = 'uploadBinary';\n existingBinary: File;\n submitButtonTitle: string;\n textForConfigurationUrlPopover: string =\n gettext(`Path for binaries can vary depending on device agent implementation, for example:\n /configuration/binaries/configuration1.bin\n https://configuration/binary/123\n ftp://configuration/binary/123.tar.gz\n Configurations with external URLs only work with the configuration typed devices (file-based configuration), not with devices with a legacy configuration.\n `);\n\n result: Promise<void> = new Promise((resolve, reject) => {\n this._save = resolve;\n this._cancel = reject;\n });\n ValidationPattern = ValidationPattern;\n\n private _save;\n private _cancel;\n\n constructor(\n private repositoryService: RepositoryService,\n private bsModalRef: BsModalRef,\n private alert: AlertService\n ) {}\n\n async ngOnInit() {\n this.configs = await this.repositoryService.listRepositoryEntries(RepositoryType.CONFIGURATION);\n if (this.mo) {\n this.uploadChoice = this.binary.file ? 'uploadBinary' : 'uploadUrl';\n this.existingBinary = this.binary.file;\n this.configurationTypeMO = this.mo;\n }\n this.setPipe('');\n this.submitButtonTitle = this.mo.id\n ? gettext('Update configuration')\n : gettext('Add configuration');\n }\n\n cancel() {\n this.bsModalRef.hide();\n this._cancel();\n }\n\n setPipe(filterStr: string) {\n this.pattern = filterStr;\n this.filterPipe = pipe(\n map(data => uniqBy(data, 'configurationType')),\n map(data => {\n return data.filter(\n mo =>\n mo.configurationType &&\n mo.configurationType.toLowerCase().indexOf(filterStr.toLowerCase()) > -1\n );\n })\n );\n }\n\n onFile(dropped: PickedFiles) {\n this.configurationForm.form.markAsDirty();\n if (!isUndefined(dropped.url)) {\n this.binary = {\n url: dropped.url\n };\n return;\n } else if (dropped.droppedFiles) {\n this.binary = {\n file: dropped.droppedFiles[0].file\n };\n return;\n } else {\n this.binary = {\n file: undefined,\n url: undefined\n };\n }\n }\n\n async save() {\n try {\n this.saving = true;\n const { version, description, binary, deviceType } = this;\n if (this.existingBinary === this.binary.file) {\n binary.file = undefined;\n }\n await this.repositoryService.create(\n {\n version,\n description,\n binary,\n deviceType,\n configurationType: this.configurationTypeMO?.configurationType\n },\n RepositoryType.CONFIGURATION,\n this.mo\n );\n this.alert.success(\n this.mo.id ? gettext('Configuration updated.') : gettext('Configuration created.')\n );\n this.bsModalRef.hide();\n this._save();\n } catch (ex) {\n this.alert.addServerFailure(ex);\n this._cancel();\n } finally {\n this.saving = false;\n }\n }\n}\n","<div class=\"viewport-modal\">\n <div\n class=\"modal-header dialog-header\"\n id=\"configurationModalTitle\"\n >\n <i [c8yIcon]=\"'cogs'\"></i>\n <h4\n id=\"modal-title\"\n translate\n *ngIf=\"mo.id\"\n >\n Update configuration\n </h4>\n <h4\n id=\"modal-title\"\n translate\n *ngIf=\"!mo.id\"\n >\n Add configuration\n </h4>\n </div>\n\n <form\n class=\"d-contents\"\n #configurationForm=\"ngForm\"\n (ngSubmit)=\"configurationForm.form.valid && save()\"\n >\n <div\n class=\"modal-inner-scroll\"\n id=\"modal-body\"\n >\n <div\n class=\"modal-body\"\n id=\"configurationModalDescription\"\n >\n <c8y-form-group>\n <label translate>Name</label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} hosts\"\n name=\"version\"\n type=\"text\"\n autocomplete=\"off\"\n required\n maxlength=\"254\"\n [(ngModel)]=\"version\"\n [pattern]=\"ValidationPattern.rules.noWhiteSpaceOnly.pattern\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate>Device type</label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} c8y_Linux\"\n name=\"deviceType\"\n type=\"text\"\n autocomplete=\"off\"\n maxlength=\"254\"\n [(ngModel)]=\"deviceType\"\n [pattern]=\"ValidationPattern.rules.noWhiteSpaceOnly.pattern\"\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. Host configuration' | translate }} c8y_Linux\"\n name=\"description\"\n type=\"text\"\n autocomplete=\"off\"\n maxlength=\"254\"\n [(ngModel)]=\"description\"\n [pattern]=\"ValidationPattern.rules.noWhiteSpaceOnly.pattern\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate>Configuration type</label>\n <c8y-typeahead\n placeholder=\"{{ 'e.g.' | translate }} ssh\"\n name=\"confType\"\n [(ngModel)]=\"configurationTypeMO\"\n maxlength=\"254\"\n (onSearch)=\"setPipe($event)\"\n displayProperty=\"configurationType\"\n >\n <c8y-li\n class=\"p-l-8 p-r-8 c8y-list__item--link\"\n *c8yFor=\"let config of configs; pipe: filterPipe; notFound: notFoundTemplate\"\n (click)=\"configurationTypeMO = config; setPipe('')\"\n [active]=\"configurationTypeMO === config\"\n >\n <c8y-highlight\n [text]=\"config.configurationType || '--'\"\n [pattern]=\"pattern\"\n ></c8y-highlight>\n </c8y-li>\n <ng-template #notFoundTemplate>\n <c8y-li\n class=\"bg-level-2 p-8\"\n *ngIf=\"pattern.length > 0\"\n >\n <span translate>No match found.</span>\n <button\n class=\"btn btn-primary btn-xs m-l-8\"\n title=\"{{ 'Add new`configuration type`' | translate }}\"\n type=\"button\"\n translate\n >\n Add new`configuration type`\n </button>\n </c8y-li>\n </ng-template>\n </c8y-typeahead>\n </c8y-form-group>\n\n <c8y-form-group>\n <div\n class=\"legend form-block m-t-40\"\n translate\n >\n Configuration file\n </div>\n <c8y-file-picker\n [maxAllowedFiles]=\"1\"\n (onFilesPicked)=\"onFile($event)\"\n [uploadChoice]=\"uploadChoice\"\n [fileUrl]=\"binary.url\"\n [fileBinary]=\"binary.file\"\n [fileUrlPopover]=\"textForConfigurationUrlPopover\"\n ></c8y-file-picker>\n </c8y-form-group>\n </div>\n </div>\n\n <div class=\"modal-footer\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"cancel()\"\n [disabled]=\"saving\"\n >\n <span translate>Cancel</span>\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ submitButtonTitle | translate }}\"\n type=\"submit\"\n [ngClass]=\"{ 'btn-pending': saving }\"\n [disabled]=\"\n !configurationForm.valid ||\n configurationForm.pristine ||\n (!binary?.url?.trim() && !binary?.file) ||\n saving\n \"\n >\n {{ submitButtonTitle | translate }}\n </button>\n </div>\n </form>\n</div>\n","import { Component, EventEmitter, OnInit, ViewChild } from '@angular/core';\nimport { IManagedObject, InventoryBinaryService, IResultList } from '@c8y/client';\nimport {\n ActionControl,\n AlertService,\n BuiltInActionType,\n Column,\n DataGridService,\n DataSourceModifier,\n FilterInputComponent,\n gettext,\n ModalService,\n ServerSideDataCallback,\n ServerSideDataResult,\n Status\n} from '@c8y/ngx-components';\nimport {\n DescriptionGridColumn,\n DeviceTypeGridColumn,\n FileGridColumn,\n RepositoryItemNameGridColumn,\n RepositoryService,\n RepositoryType,\n TypeGridColumn\n} from '@c8y/ngx-components/repository/shared';\nimport { Translate