@c8y/ngx-components 1023.92.0 → 1023.97.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai/agents/scada/index.d.ts +10 -0
- package/ai/agents/scada/index.d.ts.map +1 -0
- package/ai/ai-chat/index.d.ts +1 -1
- package/ai/ai-chat/index.d.ts.map +1 -1
- package/alarms/index.d.ts.map +1 -1
- package/computed-asset-properties/index.d.ts +89 -1
- package/computed-asset-properties/index.d.ts.map +1 -1
- package/context-dashboard/device/view/index.d.ts +24 -1
- package/context-dashboard/device/view/index.d.ts.map +1 -1
- package/context-dashboard/index.d.ts +27 -11
- package/context-dashboard/index.d.ts.map +1 -1
- package/echart/index.d.ts.map +1 -1
- package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs +2 -2
- package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ai-agents-scada.mjs +893 -0
- package/fesm2022/c8y-ngx-components-ai-agents-scada.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs +1 -0
- package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-alarms.mjs +12 -5
- package/fesm2022/c8y-ngx-components-alarms.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-asset-properties.mjs +2 -2
- package/fesm2022/c8y-ngx-components-asset-properties.mjs.map +1 -1
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-Cy9R6Ixb.mjs → c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-CHZX7ALq.mjs} +10 -9
- package/fesm2022/c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-CHZX7ALq.mjs.map +1 -0
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-LrmlAh6V.mjs → c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-DgWGXzEx.mjs} +2 -2
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-LrmlAh6V.mjs.map → c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-DgWGXzEx.mjs.map} +1 -1
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-last-measurement-config.component-f1u1TcCl.mjs → c8y-ngx-components-computed-asset-properties-last-measurement-config.component-yfc45-l_.mjs} +2 -2
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-last-measurement-config.component-f1u1TcCl.mjs.map → c8y-ngx-components-computed-asset-properties-last-measurement-config.component-yfc45-l_.mjs.map} +1 -1
- package/fesm2022/c8y-ngx-components-computed-asset-properties.mjs +1 -1
- package/fesm2022/c8y-ngx-components-context-dashboard-device-view.mjs +21 -3
- package/fesm2022/c8y-ngx-components-context-dashboard-device-view.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-context-dashboard-devicemanagement.mjs +2 -2
- package/fesm2022/c8y-ngx-components-context-dashboard-devicemanagement.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs +69 -51
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-echart.mjs +52 -36
- package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-global-context.mjs +112 -22
- package/fesm2022/c8y-ngx-components-global-context.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-map.mjs +15 -0
- package/fesm2022/c8y-ngx-components-map.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-repository-configuration.mjs +292 -115
- package/fesm2022/c8y-ngx-components-repository-configuration.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-repository-firmware.mjs +2 -2
- package/fesm2022/c8y-ngx-components-repository-firmware.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-repository-software.mjs +1 -1
- package/fesm2022/c8y-ngx-components-repository-software.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-upgrade.mjs +12 -1
- package/fesm2022/c8y-ngx-components-upgrade.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget.mjs +2 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions-scada.mjs +169 -0
- package/fesm2022/c8y-ngx-components-widgets-definitions-scada.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-widgets-definitions.mjs +2 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-exports.mjs +8 -1
- package/fesm2022/c8y-ngx-components-widgets-exports.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-alarms.mjs +2 -2
- package/fesm2022/c8y-ngx-components-widgets-implementations-alarms.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs +3 -3
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-list.mjs +2 -2
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-list.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs +2 -2
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-events.mjs +2 -2
- package/fesm2022/c8y-ngx-components-widgets-implementations-events.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs +90 -32
- package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-kpi.mjs +2 -2
- package/fesm2022/c8y-ngx-components-widgets-implementations-kpi.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs +1698 -0
- package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components.mjs +220 -45
- package/fesm2022/c8y-ngx-components.mjs.map +1 -1
- package/global-context/index.d.ts +58 -3
- package/global-context/index.d.ts.map +1 -1
- package/index.d.ts +79 -22
- package/index.d.ts.map +1 -1
- package/locales/de.po +165 -3
- package/locales/es.po +165 -3
- package/locales/fr.po +165 -3
- package/locales/ja_JP.po +165 -3
- package/locales/ko.po +165 -3
- package/locales/locales.pot +156 -3
- package/locales/nl.po +165 -3
- package/locales/pl.po +165 -3
- package/locales/pt_BR.po +165 -3
- package/locales/zh_CN.po +165 -3
- package/locales/zh_TW.po +165 -3
- package/map/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/repository/configuration/index.d.ts +64 -39
- package/repository/configuration/index.d.ts.map +1 -1
- package/upgrade/index.d.ts +1 -0
- package/upgrade/index.d.ts.map +1 -1
- package/widgets/cockpit-exports/index.d.ts +6 -0
- package/widgets/cockpit-exports/index.d.ts.map +1 -1
- package/widgets/definitions/index.d.ts +1 -0
- package/widgets/definitions/index.d.ts.map +1 -1
- package/widgets/definitions/scada/index.d.ts +6 -0
- package/widgets/definitions/scada/index.d.ts.map +1 -0
- package/widgets/device-management-exports/index.d.ts +6 -0
- package/widgets/device-management-exports/index.d.ts.map +1 -1
- package/widgets/exports/index.d.ts +8 -1
- package/widgets/exports/index.d.ts.map +1 -1
- package/widgets/implementations/html-widget/index.d.ts +30 -12
- package/widgets/implementations/html-widget/index.d.ts.map +1 -1
- package/widgets/implementations/scada/index.d.ts +415 -0
- package/widgets/implementations/scada/index.d.ts.map +1 -0
- package/fesm2022/c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-Cy9R6Ixb.mjs.map +0 -1
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { EventEmitter, Injectable, Component, Pipe, Input, Output, ViewChild, NgModule } from '@angular/core';
|
|
2
|
+
import { EventEmitter, Injectable, Component, Pipe, Input, Output, ViewChild, NgModule, inject, ChangeDetectorRef, signal, computed, ChangeDetectionStrategy } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/router';
|
|
4
4
|
import * as i4 from '@c8y/client';
|
|
5
|
-
import { OperationStatus } from '@c8y/client';
|
|
5
|
+
import { OperationStatus, InventoryBinaryService } from '@c8y/client';
|
|
6
6
|
import { gettext } from '@c8y/ngx-components/gettext';
|
|
7
7
|
import * as i2 from '@c8y/ngx-components';
|
|
8
|
-
import { IconDirective, C8yTranslatePipe, C8yTranslateDirective, FormGroupComponent, RequiredInputPlaceholderDirective, Permissions, DatePipe, FilterInputComponent, ActionBarItemComponent, TabsetAriaDirective, EmptyStateComponent, hookRoute, ViewContext, ValidationPattern, TypeaheadComponent, ForOfDirective, ListItemComponent, HighlightComponent, FilePickerComponent, BuiltInActionType, Status, TitleComponent, BreadcrumbComponent, BreadcrumbItemComponent, HelpComponent, DataGridComponent, EmptyStateContextDirective, GuideDocsComponent, GuideHrefDirective, NavigatorNode, hookNavigator, CoreModule, FormsModule as FormsModule$1 } from '@c8y/ngx-components';
|
|
8
|
+
import { IconDirective, C8yTranslatePipe, C8yTranslateDirective, FormGroupComponent, RequiredInputPlaceholderDirective, Permissions, DatePipe, FilterInputComponent, ActionBarItemComponent, TabsetAriaDirective, EmptyStateComponent, hookRoute, ViewContext, BottomDrawerRef, AlertService, ValidationPattern, isBinaryFile, TypeaheadComponent, ForOfDirective, ListItemComponent, HighlightComponent, FilePickerComponent, MessagesComponent, SelectComponent, DataGridService, BottomDrawerService, ModalService, BuiltInActionType, Status, TitleComponent, BreadcrumbComponent, BreadcrumbItemComponent, HelpComponent, DataGridComponent, EmptyStateContextDirective, GuideDocsComponent, GuideHrefDirective, NavigatorNode, hookNavigator, CoreModule, FormsModule as FormsModule$1 } from '@c8y/ngx-components';
|
|
9
9
|
import * as i3 from '@c8y/ngx-components/repository/shared';
|
|
10
|
-
import { DeviceConfigurationOperation, RepositoryType, SharedRepositoryModule, RepositoryItemNameGridColumn, DescriptionGridColumn, FileGridColumn, DeviceTypeGridColumn, TypeGridColumn } from '@c8y/ngx-components/repository/shared';
|
|
10
|
+
import { DeviceConfigurationOperation, RepositoryType, SharedRepositoryModule, RepositoryService, RepositoryItemNameGridColumn, DescriptionGridColumn, FileGridColumn, DeviceTypeGridColumn, TypeGridColumn } from '@c8y/ngx-components/repository/shared';
|
|
11
11
|
import { NgIf, NgClass, NgFor, AsyncPipe } from '@angular/common';
|
|
12
12
|
import * as i4$1 from '@angular/forms';
|
|
13
|
-
import { FormsModule } from '@angular/forms';
|
|
13
|
+
import { FormsModule, FormGroup, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
14
14
|
import { OperationDetailsComponent, OperationDetailsModule } from '@c8y/ngx-components/operations/operation-details';
|
|
15
|
-
import { has, cloneDeep, uniqBy
|
|
15
|
+
import { has, cloneDeep, uniqBy } from 'lodash-es';
|
|
16
16
|
import { saveAs } from 'file-saver';
|
|
17
|
-
import * as
|
|
17
|
+
import * as i1$1 from 'ngx-bootstrap/modal';
|
|
18
18
|
import { map } from 'rxjs/operators';
|
|
19
|
-
import * as i1$
|
|
19
|
+
import * as i1$2 from 'ngx-bootstrap/tabs';
|
|
20
20
|
import { TabsetComponent, TabDirective, TabsModule } from 'ngx-bootstrap/tabs';
|
|
21
|
-
import
|
|
21
|
+
import { TranslateService } from '@ngx-translate/core';
|
|
22
|
+
import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
23
|
+
import { EditorComponent, MonacoEditorMarkerValidatorDirective } from '@c8y/ngx-components/editor';
|
|
22
24
|
import { pipe } from 'rxjs';
|
|
23
25
|
|
|
24
26
|
class DeviceConfigurationService {
|
|
@@ -232,7 +234,7 @@ class SaveToRepositoryComponent {
|
|
|
232
234
|
this._cancel();
|
|
233
235
|
this.modal.hide();
|
|
234
236
|
}
|
|
235
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SaveToRepositoryComponent, deps: [{ token:
|
|
237
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SaveToRepositoryComponent, deps: [{ token: i1$1.BsModalRef }, { token: i2.AlertService }, { token: i3.RepositoryService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
236
238
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.25", type: SaveToRepositoryComponent, isStandalone: true, selector: "c8y-save-config-to-configuration-repository", ngImport: i0, template: "<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", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4$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: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i4$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i4$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
237
239
|
}
|
|
238
240
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SaveToRepositoryComponent, decorators: [{
|
|
@@ -245,7 +247,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
245
247
|
RequiredInputPlaceholderDirective,
|
|
246
248
|
C8yTranslatePipe
|
|
247
249
|
], template: "<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" }]
|
|
248
|
-
}], ctorParameters: () => [{ type:
|
|
250
|
+
}], ctorParameters: () => [{ type: i1$1.BsModalRef }, { type: i2.AlertService }, { type: i3.RepositoryService }] });
|
|
249
251
|
|
|
250
252
|
class SourceCodePreviewComponent {
|
|
251
253
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SourceCodePreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
@@ -407,7 +409,7 @@ class ConfigurationPreviewComponent {
|
|
|
407
409
|
this.deviceConfigurationService.updateConfigurations();
|
|
408
410
|
}
|
|
409
411
|
}
|
|
410
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ConfigurationPreviewComponent, deps: [{ token: DeviceConfigurationService }, { token: i2.OperationRealtimeService }, { token:
|
|
412
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ConfigurationPreviewComponent, deps: [{ token: DeviceConfigurationService }, { token: i2.OperationRealtimeService }, { token: i1$1.BsModalService }, { token: i4.UserService }, { token: i2.AppStateService }, { token: i3.RepositoryService }, { token: i4.OperationService }, { token: i2.AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
411
413
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.25", type: ConfigurationPreviewComponent, isStandalone: true, selector: "c8y-device-configuration-preview", inputs: { device: "device", configurationType: "configurationType", configSnapshot: "configSnapshot", canSaveSnapshot: "canSaveSnapshot", actionButtonText: "actionButtonText", actionButtonIcon: "actionButtonIcon", isLegacy: "isLegacy", operationToTrigger: "operationToTrigger" }, ngImport: i0, template: "<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", dependencies: [{ kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: SourceCodePreviewComponent, selector: "c8y-source-code-preview", inputs: ["isDisabled", "text"] }, { kind: "component", type: OperationDetailsComponent, selector: "c8y-operation-details", inputs: ["operation"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }] }); }
|
|
412
414
|
}
|
|
413
415
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ConfigurationPreviewComponent, decorators: [{
|
|
@@ -421,7 +423,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
421
423
|
C8yTranslatePipe,
|
|
422
424
|
DatePipe
|
|
423
425
|
], template: "<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" }]
|
|
424
|
-
}], ctorParameters: () => [{ type: DeviceConfigurationService }, { type: i2.OperationRealtimeService }, { type:
|
|
426
|
+
}], ctorParameters: () => [{ type: DeviceConfigurationService }, { type: i2.OperationRealtimeService }, { type: i1$1.BsModalService }, { type: i4.UserService }, { type: i2.AppStateService }, { type: i3.RepositoryService }, { type: i4.OperationService }, { type: i2.AlertService }], propDecorators: { device: [{
|
|
425
427
|
type: Input
|
|
426
428
|
}], configurationType: [{
|
|
427
429
|
type: Input
|
|
@@ -607,7 +609,7 @@ class ConfigurationRepositoryDeviceTabModule {
|
|
|
607
609
|
}
|
|
608
610
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ConfigurationRepositoryDeviceTabModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
609
611
|
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: ConfigurationRepositoryDeviceTabModule, imports: [SharedRepositoryModule,
|
|
610
|
-
OperationDetailsModule, i1$
|
|
612
|
+
OperationDetailsModule, i1$2.TabsModule, DeviceConfigurationComponent,
|
|
611
613
|
DeviceConfigurationListComponent,
|
|
612
614
|
ConfigurationPreviewComponent,
|
|
613
615
|
ConfigurationFilterPipe,
|
|
@@ -642,19 +644,95 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
642
644
|
}]
|
|
643
645
|
}] });
|
|
644
646
|
|
|
647
|
+
const EXT_LANGUAGE_MAP = {
|
|
648
|
+
json: 'json',
|
|
649
|
+
yaml: 'yaml',
|
|
650
|
+
yml: 'yaml',
|
|
651
|
+
xml: 'xml',
|
|
652
|
+
html: 'html',
|
|
653
|
+
css: 'css',
|
|
654
|
+
ts: 'typescript',
|
|
655
|
+
js: 'javascript',
|
|
656
|
+
md: 'markdown',
|
|
657
|
+
toml: 'ini',
|
|
658
|
+
properties: 'ini',
|
|
659
|
+
ini: 'ini',
|
|
660
|
+
conf: 'ini',
|
|
661
|
+
cfg: 'ini',
|
|
662
|
+
sh: 'shell'
|
|
663
|
+
};
|
|
664
|
+
function detectLanguage(filename) {
|
|
665
|
+
const ext = (filename.split('.').pop() ?? '').toLowerCase();
|
|
666
|
+
return EXT_LANGUAGE_MAP[ext] ?? 'plaintext';
|
|
667
|
+
}
|
|
668
|
+
const EXT_MIME_MAP = {
|
|
669
|
+
json: 'application/json',
|
|
670
|
+
xml: 'application/xml',
|
|
671
|
+
yaml: 'application/yaml',
|
|
672
|
+
yml: 'application/yaml',
|
|
673
|
+
js: 'application/javascript',
|
|
674
|
+
ts: 'application/typescript',
|
|
675
|
+
html: 'text/html',
|
|
676
|
+
css: 'text/css',
|
|
677
|
+
md: 'text/markdown',
|
|
678
|
+
sh: 'application/x-sh',
|
|
679
|
+
toml: 'application/toml'
|
|
680
|
+
};
|
|
681
|
+
function mimeForFilename(filename) {
|
|
682
|
+
const ext = (filename.split('.').pop() ?? '').toLowerCase();
|
|
683
|
+
return EXT_MIME_MAP[ext] ?? 'text/plain';
|
|
684
|
+
}
|
|
685
|
+
/** Sentinel value meaning "derive language from filename". */
|
|
686
|
+
const AUTO_LANGUAGE = 'auto';
|
|
687
|
+
const EDITOR_LANGUAGES = [
|
|
688
|
+
{ value: AUTO_LANGUAGE, label: gettext('Auto (from filename)') },
|
|
689
|
+
{ value: 'json', label: gettext('JSON') },
|
|
690
|
+
{ value: 'javascript', label: gettext('JavaScript') },
|
|
691
|
+
{ value: 'typescript', label: gettext('TypeScript') },
|
|
692
|
+
{ value: 'css', label: gettext('CSS') },
|
|
693
|
+
{ value: 'html', label: gettext('HTML') },
|
|
694
|
+
{ value: 'yaml', label: gettext('YAML') },
|
|
695
|
+
{ value: 'markdown', label: gettext('Markdown') },
|
|
696
|
+
{ value: 'xml', label: gettext('XML') },
|
|
697
|
+
{ value: 'ini', label: gettext('INI / TOML / Properties') },
|
|
698
|
+
{ value: 'shell', label: gettext('Shell') },
|
|
699
|
+
{ value: 'plaintext', label: gettext('Plain text') }
|
|
700
|
+
];
|
|
645
701
|
class ConfigurationDetailComponent {
|
|
646
|
-
constructor(
|
|
647
|
-
this.repositoryService =
|
|
648
|
-
this.
|
|
649
|
-
this.alert =
|
|
650
|
-
this.
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
};
|
|
654
|
-
this.pattern = '';
|
|
702
|
+
constructor() {
|
|
703
|
+
this.repositoryService = inject(RepositoryService);
|
|
704
|
+
this.bottomDrawerRef = inject(BottomDrawerRef);
|
|
705
|
+
this.alert = inject(AlertService);
|
|
706
|
+
this.inventoryBinaryService = inject(InventoryBinaryService);
|
|
707
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
708
|
+
this.binary = { file: undefined, url: undefined };
|
|
655
709
|
this.mo = {};
|
|
656
|
-
this.
|
|
657
|
-
|
|
710
|
+
this.form = new FormGroup({
|
|
711
|
+
version: new FormControl('', [
|
|
712
|
+
Validators.required,
|
|
713
|
+
Validators.maxLength(254),
|
|
714
|
+
Validators.pattern(ValidationPattern.rules.noWhiteSpaceOnly.pattern)
|
|
715
|
+
]),
|
|
716
|
+
deviceType: new FormControl('', [
|
|
717
|
+
Validators.maxLength(254),
|
|
718
|
+
Validators.pattern(ValidationPattern.rules.noWhiteSpaceOnly.pattern)
|
|
719
|
+
]),
|
|
720
|
+
description: new FormControl('', [
|
|
721
|
+
Validators.maxLength(254),
|
|
722
|
+
Validators.pattern(ValidationPattern.rules.noWhiteSpaceOnly.pattern)
|
|
723
|
+
]),
|
|
724
|
+
uploadChoice: new FormControl('uploadBinary'),
|
|
725
|
+
editorFilename: new FormControl('', [
|
|
726
|
+
Validators.pattern(ValidationPattern.rules.noWhiteSpaceOnly.pattern)
|
|
727
|
+
]),
|
|
728
|
+
editorLanguage: new FormControl(EDITOR_LANGUAGES[0]),
|
|
729
|
+
editorContent: new FormControl('')
|
|
730
|
+
});
|
|
731
|
+
this.configurationTypeMO = {};
|
|
732
|
+
this.saving = signal(false, ...(ngDevMode ? [{ debugName: "saving" }] : []));
|
|
733
|
+
this.pattern = signal('', ...(ngDevMode ? [{ debugName: "pattern" }] : []));
|
|
734
|
+
this.isExternalUrl = signal(false, ...(ngDevMode ? [{ debugName: "isExternalUrl" }] : []));
|
|
735
|
+
this.ValidationPattern = ValidationPattern;
|
|
658
736
|
this.textForConfigurationUrlPopover = gettext(`Path for binaries can vary depending on device agent implementation, for example:
|
|
659
737
|
/configuration/binaries/configuration1.bin
|
|
660
738
|
https://configuration/binary/123
|
|
@@ -665,58 +743,146 @@ class ConfigurationDetailComponent {
|
|
|
665
743
|
this._save = resolve;
|
|
666
744
|
this._cancel = reject;
|
|
667
745
|
});
|
|
668
|
-
this.
|
|
746
|
+
this.settled = false;
|
|
747
|
+
this.editorFilenameValue = toSignal(this.form.controls.editorFilename.valueChanges, {
|
|
748
|
+
initialValue: ''
|
|
749
|
+
});
|
|
750
|
+
this.editorLanguageValue = toSignal(this.form.controls.editorLanguage.valueChanges, {
|
|
751
|
+
initialValue: EDITOR_LANGUAGES[0]
|
|
752
|
+
});
|
|
753
|
+
/** User's manual pick (when not "auto") wins over auto-detection from filename. */
|
|
754
|
+
this.editorLanguage = computed(() => {
|
|
755
|
+
const picked = this.editorLanguageValue()?.value;
|
|
756
|
+
return !picked || picked === AUTO_LANGUAGE
|
|
757
|
+
? detectLanguage(this.editorFilenameValue() ?? '')
|
|
758
|
+
: picked;
|
|
759
|
+
}, ...(ngDevMode ? [{ debugName: "editorLanguage" }] : []));
|
|
760
|
+
this.editorLanguages = EDITOR_LANGUAGES;
|
|
761
|
+
this.submitButtonTitle = computed(() => this.mo?.id ? gettext('Update configuration') : gettext('Add configuration'), ...(ngDevMode ? [{ debugName: "submitButtonTitle" }] : []));
|
|
762
|
+
this.form.controls.uploadChoice.valueChanges.pipe(takeUntilDestroyed()).subscribe(choice => {
|
|
763
|
+
const ctrl = this.form.controls.editorFilename;
|
|
764
|
+
if (choice === 'editInline') {
|
|
765
|
+
ctrl.addValidators(Validators.required);
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
ctrl.removeValidators(Validators.required);
|
|
769
|
+
}
|
|
770
|
+
ctrl.updateValueAndValidity();
|
|
771
|
+
});
|
|
772
|
+
// Drawer can be closed externally (Escape, navigation, programmatic close).
|
|
773
|
+
// Make sure the result promise is always settled, otherwise callers awaiting it hang.
|
|
774
|
+
this.bottomDrawerRef.onClosed$.pipe(takeUntilDestroyed()).subscribe(() => {
|
|
775
|
+
if (!this.settled) {
|
|
776
|
+
this.settled = true;
|
|
777
|
+
this._cancel('Drawer closed');
|
|
778
|
+
}
|
|
779
|
+
});
|
|
669
780
|
}
|
|
670
781
|
async ngOnInit() {
|
|
671
782
|
this.configs = await this.repositoryService.listRepositoryEntries(RepositoryType.CONFIGURATION);
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
this.existingBinary = this.binary.file;
|
|
783
|
+
this.setPipe('');
|
|
784
|
+
if (this.mo?.id) {
|
|
675
785
|
this.configurationTypeMO = this.mo;
|
|
786
|
+
const hasExternalUrl = !!this.binary?.url && !this.inventoryBinaryService.getIdFromUrl(this.binary.url);
|
|
787
|
+
if (hasExternalUrl) {
|
|
788
|
+
this.isExternalUrl.set(true);
|
|
789
|
+
this.form.patchValue({
|
|
790
|
+
version: this.version ?? '',
|
|
791
|
+
deviceType: this.deviceType ?? '',
|
|
792
|
+
description: this.description ?? '',
|
|
793
|
+
uploadChoice: 'uploadUrl'
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
const patchValues = {
|
|
798
|
+
version: this.version ?? '',
|
|
799
|
+
deviceType: this.deviceType ?? '',
|
|
800
|
+
description: this.description ?? '',
|
|
801
|
+
uploadChoice: 'editInline'
|
|
802
|
+
};
|
|
803
|
+
if (this.binary?.file) {
|
|
804
|
+
this.existingBinary = this.binary.file;
|
|
805
|
+
if (await isBinaryFile(this.binary.file)) {
|
|
806
|
+
patchValues.uploadChoice = 'uploadBinary';
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
patchValues.editorContent = await this.binary.file.text();
|
|
810
|
+
patchValues.editorFilename = this.binary.file.name;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
this.form.patchValue(patchValues);
|
|
814
|
+
}
|
|
815
|
+
this.form.markAsDirty();
|
|
676
816
|
}
|
|
677
|
-
this.
|
|
678
|
-
this.submitButtonTitle = this.mo.id
|
|
679
|
-
? gettext('Update configuration')
|
|
680
|
-
: gettext('Add configuration');
|
|
817
|
+
this.cdr.markForCheck();
|
|
681
818
|
}
|
|
682
819
|
cancel() {
|
|
683
|
-
this.
|
|
684
|
-
this._cancel();
|
|
820
|
+
this.settled = true;
|
|
821
|
+
this._cancel('User canceled');
|
|
822
|
+
this.bottomDrawerRef.close();
|
|
685
823
|
}
|
|
686
824
|
setPipe(filterStr) {
|
|
687
|
-
this.pattern
|
|
688
|
-
this.filterPipe = pipe(map(data => uniqBy(data, 'configurationType')), map(data =>
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
825
|
+
this.pattern.set(filterStr);
|
|
826
|
+
this.filterPipe = pipe(map(data => uniqBy(data, 'configurationType')), map(data => data.filter(mo => mo.configurationType &&
|
|
827
|
+
mo.configurationType.toLowerCase().indexOf(filterStr.toLowerCase()) > -1)));
|
|
828
|
+
}
|
|
829
|
+
onUploadChoiceChange(choice) {
|
|
830
|
+
this.form.controls.uploadChoice.setValue(choice);
|
|
831
|
+
this.form.markAsDirty();
|
|
832
|
+
}
|
|
833
|
+
onFileBinaryChange(file) {
|
|
834
|
+
this.binary = { ...this.binary, file };
|
|
835
|
+
this.form.markAsDirty();
|
|
836
|
+
this.cdr.markForCheck();
|
|
837
|
+
}
|
|
838
|
+
onFileUrlChange(url) {
|
|
839
|
+
this.binary = { ...this.binary, url };
|
|
840
|
+
this.form.markAsDirty();
|
|
841
|
+
this.cdr.markForCheck();
|
|
842
|
+
}
|
|
843
|
+
onTextFilePicked({ name, content }) {
|
|
844
|
+
this.form.patchValue({
|
|
845
|
+
uploadChoice: 'editInline',
|
|
846
|
+
editorFilename: name,
|
|
847
|
+
editorContent: content
|
|
848
|
+
});
|
|
849
|
+
this.form.markAsDirty();
|
|
850
|
+
this.cdr.markForCheck();
|
|
851
|
+
}
|
|
852
|
+
get isSaveDisabled() {
|
|
853
|
+
if (this.saving() || !this.form.valid || this.form.pristine) {
|
|
854
|
+
return true;
|
|
700
855
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
};
|
|
705
|
-
return;
|
|
856
|
+
const { uploadChoice, editorFilename, editorContent } = this.form.value;
|
|
857
|
+
if (uploadChoice === 'uploadBinary') {
|
|
858
|
+
return !this.binary?.file;
|
|
706
859
|
}
|
|
707
|
-
|
|
708
|
-
this.binary
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
860
|
+
if (uploadChoice === 'uploadUrl') {
|
|
861
|
+
return !this.binary?.url?.trim();
|
|
862
|
+
}
|
|
863
|
+
if (uploadChoice === 'editInline') {
|
|
864
|
+
return !editorFilename?.trim() || !editorContent;
|
|
712
865
|
}
|
|
866
|
+
return false;
|
|
713
867
|
}
|
|
714
868
|
async save() {
|
|
869
|
+
if (this.isSaveDisabled) {
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
715
872
|
try {
|
|
716
|
-
this.saving
|
|
717
|
-
const { version, description,
|
|
718
|
-
|
|
719
|
-
|
|
873
|
+
this.saving.set(true);
|
|
874
|
+
const { version, deviceType, description, uploadChoice, editorFilename, editorContent } = this.form.value;
|
|
875
|
+
let binary = { ...this.binary };
|
|
876
|
+
if (uploadChoice === 'uploadUrl') {
|
|
877
|
+
binary = { url: this.binary?.url };
|
|
878
|
+
}
|
|
879
|
+
else if (uploadChoice === 'editInline') {
|
|
880
|
+
binary = {
|
|
881
|
+
file: new File([editorContent], editorFilename, { type: mimeForFilename(editorFilename) })
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
else if (this.existingBinary === this.binary?.file) {
|
|
885
|
+
binary = { ...this.binary, file: undefined };
|
|
720
886
|
}
|
|
721
887
|
await this.repositoryService.create({
|
|
722
888
|
version,
|
|
@@ -725,52 +891,65 @@ class ConfigurationDetailComponent {
|
|
|
725
891
|
deviceType,
|
|
726
892
|
configurationType: this.configurationTypeMO?.configurationType
|
|
727
893
|
}, RepositoryType.CONFIGURATION, this.mo);
|
|
728
|
-
this.alert.success(this.mo
|
|
729
|
-
this.
|
|
894
|
+
this.alert.success(this.mo?.id ? gettext('Configuration updated.') : gettext('Configuration created.'));
|
|
895
|
+
this.settled = true;
|
|
730
896
|
this._save();
|
|
897
|
+
this.bottomDrawerRef.close();
|
|
731
898
|
}
|
|
732
899
|
catch (ex) {
|
|
900
|
+
// Keep the drawer open so the user can fix and retry.
|
|
901
|
+
// Do NOT settle the result promise here.
|
|
733
902
|
this.alert.addServerFailure(ex);
|
|
734
|
-
this._cancel();
|
|
735
903
|
}
|
|
736
904
|
finally {
|
|
737
|
-
this.saving
|
|
905
|
+
this.saving.set(false);
|
|
738
906
|
}
|
|
739
907
|
}
|
|
740
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ConfigurationDetailComponent, deps: [
|
|
741
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.25", type: ConfigurationDetailComponent, isStandalone: true, selector: "c8y-configuration-detail", viewQueries: [{ propertyName: "configurationForm", first: true, predicate: ["configurationForm"], descendants: true, static: true }], ngImport: i0, template: "<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", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4$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: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i4$1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i4$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i4$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: TypeaheadComponent, selector: "c8y-typeahead", inputs: ["required", "maxlength", "disabled", "allowFreeEntries", "placeholder", "displayProperty", "icon", "name", "autoClose", "hideNew", "container", "selected", "title", "highlightFirstItem"], outputs: ["onSearch", "onIconClick"] }, { kind: "directive", type: ForOfDirective, selector: "[c8yFor]", inputs: ["c8yForOf", "c8yForLoadMore", "c8yForPipe", "c8yForNotFound", "c8yForMaxIterations", "c8yForLoadingTemplate", "c8yForLoadNextLabel", "c8yForLoadingLabel", "c8yForRealtime", "c8yForRealtimeOptions", "c8yForComparator", "c8yForEnableVirtualScroll", "c8yForVirtualScrollElementSize", "c8yForVirtualScrollStrategy", "c8yForVirtualScrollContainerHeight"], outputs: ["c8yForCount", "c8yForChange", "c8yForLoadMoreComponent"] }, { kind: "component", type: ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: HighlightComponent, selector: "c8y-highlight", inputs: ["pattern", "text", "elementClass", "shouldTrimPattern"] }, { kind: "component", type: FilePickerComponent, selector: "c8y-file-picker", inputs: ["maxAllowedFiles", "uploadChoice", "allowedUploadChoices", "fileUrl", "fileBinary", "config", "filePickerIndex", "fileUrlPopover"], outputs: ["onFilesPicked"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
908
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ConfigurationDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
909
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: ConfigurationDetailComponent, isStandalone: true, selector: "c8y-configuration-detail", inputs: { version: "version", description: "description", binary: "binary", deviceType: "deviceType", mo: "mo" }, host: { classAttribute: "d-contents" }, ngImport: i0, template: "<div class=\"card-header j-c-center separator flex-no-shrink p-24\">\n <i\n class=\"m-r-8\"\n [c8yIcon]=\"'cogs'\"\n aria-hidden=\"true\"\n ></i>\n <h4 id=\"drawerTitle\">\n @if (mo?.id) {\n {{ 'Update configuration' | translate }}\n } @else {\n {{ 'Add configuration' | translate }}\n }\n </h4>\n</div>\n\n<form\n class=\"d-contents\"\n [formGroup]=\"form\"\n (ngSubmit)=\"save()\"\n>\n <div\n class=\"card-body inner-scroll p-24\"\n [class.d-col]=\"form.value.uploadChoice === 'editInline'\"\n >\n <div class=\"row\">\n <c8y-form-group class=\"col-sm-3\">\n <label\n for=\"confVersion\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n id=\"confVersion\"\n placeholder=\"{{ 'e.g.' | translate }} hosts\"\n aria-required=\"true\"\n type=\"text\"\n autocomplete=\"off\"\n formControlName=\"version\"\n />\n </c8y-form-group>\n\n <c8y-form-group class=\"col-sm-3\">\n <label\n for=\"confDeviceType\"\n translate\n >\n Device type\n </label>\n <input\n class=\"form-control\"\n id=\"confDeviceType\"\n placeholder=\"{{ 'e.g.' | translate }} c8y_Linux\"\n type=\"text\"\n autocomplete=\"off\"\n formControlName=\"deviceType\"\n />\n </c8y-form-group>\n\n <c8y-form-group class=\"col-sm-3\">\n <label\n for=\"confDescription\"\n translate\n >\n Description\n </label>\n <input\n class=\"form-control\"\n id=\"confDescription\"\n placeholder=\"{{ 'e.g.' | translate }} Host configuration\"\n type=\"text\"\n autocomplete=\"off\"\n formControlName=\"description\"\n />\n </c8y-form-group>\n\n <c8y-form-group class=\"col-sm-3\">\n <label translate>Configuration type</label>\n <c8y-typeahead\n placeholder=\"{{ 'e.g.' | translate }} ssh\"\n name=\"confType\"\n data-cy=\"conf-detail--configuration-type\"\n [ngModel]=\"configurationTypeMO\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"configurationTypeMO = $event\"\n maxlength=\"254\"\n (onSearch)=\"setPipe($event)\"\n displayProperty=\"configurationType\"\n >\n @let patternStr = pattern();\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]=\"patternStr\"\n ></c8y-highlight>\n </c8y-li>\n <ng-template #notFoundTemplate>\n @if (patternStr.length > 0) {\n <c8y-li class=\"bg-level-2 p-8\">\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 }\n </ng-template>\n </c8y-typeahead>\n </c8y-form-group>\n </div>\n\n <fieldset\n class=\"d-col\"\n [class.flex-grow]=\"form.value.uploadChoice === 'editInline'\"\n [class.min-height-0]=\"form.value.uploadChoice === 'editInline'\"\n >\n <legend\n class=\"legend form-block m-t-40\"\n translate\n >\n Configuration file\n </legend>\n\n @if (isExternalUrl()) {\n <p\n class=\"text-muted m-b-8\"\n translate\n >\n Content is sourced from a remote URL and cannot be edited inline.\n </p>\n <c8y-form-group>\n <label\n for=\"confFileUrl\"\n translate\n >\n File URL\n </label>\n <input\n class=\"form-control\"\n id=\"confFileUrl\"\n placeholder=\"https://...\"\n type=\"url\"\n autocomplete=\"off\"\n [ngModel]=\"binary.url\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"binary = { url: $event }; form.markAsDirty()\"\n />\n </c8y-form-group>\n } @else {\n <c8y-file-picker\n [maxAllowedFiles]=\"1\"\n [allowedUploadChoices]=\"['uploadBinary', 'uploadUrl', 'editInline']\"\n [uploadChoice]=\"form.controls.uploadChoice.value\"\n (uploadChoiceChange)=\"onUploadChoiceChange($event)\"\n [fileUrl]=\"binary.url\"\n (fileUrlChange)=\"onFileUrlChange($event)\"\n [fileBinary]=\"binary.file\"\n (fileBinaryChange)=\"onFileBinaryChange($event)\"\n (textFilePicked)=\"onTextFilePicked($event)\"\n [fileUrlPopover]=\"textForConfigurationUrlPopover\"\n >\n <ng-container c8yInlineEditor>\n <div class=\"row\">\n <c8y-form-group class=\"col-sm-9\">\n <label\n for=\"confEditorFilename\"\n translate\n >\n Filename\n </label>\n <input\n class=\"form-control\"\n id=\"confEditorFilename\"\n placeholder=\"config.json\"\n aria-required=\"true\"\n type=\"text\"\n autocomplete=\"off\"\n formControlName=\"editorFilename\"\n />\n </c8y-form-group>\n <c8y-form-group class=\"col-sm-3\">\n <label\n for=\"confEditorLanguage\"\n translate\n >\n Language\n </label>\n <c8y-select\n aria-label=\"{{ 'Editor language' | translate }}\"\n id=\"confEditorLanguage\"\n [items]=\"editorLanguages\"\n formControlName=\"editorLanguage\"\n ></c8y-select>\n </c8y-form-group>\n </div>\n <c8y-form-group class=\"d-col flex-grow min-height-0 m-b-0\">\n <label\n for=\"confEditorContent\"\n translate\n >\n Content\n </label>\n <c8y-editor\n class=\"flex-grow\"\n style=\"min-height: 95px\"\n id=\"confEditorContent\"\n formControlName=\"editorContent\"\n monacoEditorMarkerValidator\n [editorOptions]=\"{ language: editorLanguage() }\"\n ></c8y-editor>\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </ng-container>\n </c8y-file-picker>\n }\n </fieldset>\n </div>\n\n <div class=\"card-footer j-c-between separator flex-no-shrink p-24\">\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 [class.btn-pending]=\"saving()\"\n [title]=\"submitButtonTitle() | translate\"\n type=\"submit\"\n [disabled]=\"isSaveDisabled\"\n >\n {{ submitButtonTitle() | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i4$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4$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: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i4$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i4$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: TypeaheadComponent, selector: "c8y-typeahead", inputs: ["required", "maxlength", "disabled", "allowFreeEntries", "placeholder", "displayProperty", "icon", "name", "autoClose", "hideNew", "container", "selected", "title", "highlightFirstItem"], outputs: ["onSearch", "onIconClick"] }, { kind: "directive", type: ForOfDirective, selector: "[c8yFor]", inputs: ["c8yForOf", "c8yForLoadMore", "c8yForPipe", "c8yForNotFound", "c8yForMaxIterations", "c8yForLoadingTemplate", "c8yForLoadNextLabel", "c8yForLoadingLabel", "c8yForRealtime", "c8yForRealtimeOptions", "c8yForComparator", "c8yForEnableVirtualScroll", "c8yForVirtualScrollElementSize", "c8yForVirtualScrollStrategy", "c8yForVirtualScrollContainerHeight"], outputs: ["c8yForCount", "c8yForChange", "c8yForLoadMoreComponent"] }, { kind: "component", type: ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: HighlightComponent, selector: "c8y-highlight", inputs: ["pattern", "text", "elementClass", "shouldTrimPattern"] }, { kind: "component", type: FilePickerComponent, selector: "c8y-file-picker", inputs: ["maxAllowedFiles", "uploadChoice", "allowedUploadChoices", "fileUrl", "fileBinary", "config", "filePickerIndex", "fileUrlPopover", "editInlineConfirmTitle", "editInlineConfirmMessage"], outputs: ["onFilesPicked", "uploadChoiceChange", "fileBinaryChange", "fileUrlChange", "textFilePicked"] }, { kind: "component", type: EditorComponent, selector: "c8y-editor", inputs: ["editorOptions", "theme"], outputs: ["editorInit"] }, { kind: "directive", type: MonacoEditorMarkerValidatorDirective, selector: "c8y-editor [monacoEditorMarkerValidator]" }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage", "additionalMessages"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: SelectComponent, selector: "c8y-select", inputs: ["placeholder", "items", "selected", "container", "multi", "canSelectWithSpace", "disabled", "autoClose", "insideClick", "required", "canDeselect", "name", "icon", "filterItems"], outputs: ["onSelect", "onDeselect", "onIconClick"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
742
910
|
}
|
|
743
911
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ConfigurationDetailComponent, decorators: [{
|
|
744
912
|
type: Component,
|
|
745
|
-
args: [{ selector: 'c8y-configuration-detail', imports: [
|
|
913
|
+
args: [{ selector: 'c8y-configuration-detail', changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'd-contents' }, imports: [
|
|
914
|
+
ReactiveFormsModule,
|
|
915
|
+
FormsModule,
|
|
746
916
|
IconDirective,
|
|
747
|
-
NgIf,
|
|
748
917
|
C8yTranslateDirective,
|
|
749
|
-
FormsModule,
|
|
750
918
|
FormGroupComponent,
|
|
751
|
-
RequiredInputPlaceholderDirective,
|
|
752
919
|
TypeaheadComponent,
|
|
753
920
|
ForOfDirective,
|
|
754
921
|
ListItemComponent,
|
|
755
922
|
HighlightComponent,
|
|
756
923
|
FilePickerComponent,
|
|
757
924
|
NgClass,
|
|
758
|
-
C8yTranslatePipe
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
925
|
+
C8yTranslatePipe,
|
|
926
|
+
EditorComponent,
|
|
927
|
+
MonacoEditorMarkerValidatorDirective,
|
|
928
|
+
MessagesComponent,
|
|
929
|
+
RequiredInputPlaceholderDirective,
|
|
930
|
+
SelectComponent
|
|
931
|
+
], template: "<div class=\"card-header j-c-center separator flex-no-shrink p-24\">\n <i\n class=\"m-r-8\"\n [c8yIcon]=\"'cogs'\"\n aria-hidden=\"true\"\n ></i>\n <h4 id=\"drawerTitle\">\n @if (mo?.id) {\n {{ 'Update configuration' | translate }}\n } @else {\n {{ 'Add configuration' | translate }}\n }\n </h4>\n</div>\n\n<form\n class=\"d-contents\"\n [formGroup]=\"form\"\n (ngSubmit)=\"save()\"\n>\n <div\n class=\"card-body inner-scroll p-24\"\n [class.d-col]=\"form.value.uploadChoice === 'editInline'\"\n >\n <div class=\"row\">\n <c8y-form-group class=\"col-sm-3\">\n <label\n for=\"confVersion\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n id=\"confVersion\"\n placeholder=\"{{ 'e.g.' | translate }} hosts\"\n aria-required=\"true\"\n type=\"text\"\n autocomplete=\"off\"\n formControlName=\"version\"\n />\n </c8y-form-group>\n\n <c8y-form-group class=\"col-sm-3\">\n <label\n for=\"confDeviceType\"\n translate\n >\n Device type\n </label>\n <input\n class=\"form-control\"\n id=\"confDeviceType\"\n placeholder=\"{{ 'e.g.' | translate }} c8y_Linux\"\n type=\"text\"\n autocomplete=\"off\"\n formControlName=\"deviceType\"\n />\n </c8y-form-group>\n\n <c8y-form-group class=\"col-sm-3\">\n <label\n for=\"confDescription\"\n translate\n >\n Description\n </label>\n <input\n class=\"form-control\"\n id=\"confDescription\"\n placeholder=\"{{ 'e.g.' | translate }} Host configuration\"\n type=\"text\"\n autocomplete=\"off\"\n formControlName=\"description\"\n />\n </c8y-form-group>\n\n <c8y-form-group class=\"col-sm-3\">\n <label translate>Configuration type</label>\n <c8y-typeahead\n placeholder=\"{{ 'e.g.' | translate }} ssh\"\n name=\"confType\"\n data-cy=\"conf-detail--configuration-type\"\n [ngModel]=\"configurationTypeMO\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"configurationTypeMO = $event\"\n maxlength=\"254\"\n (onSearch)=\"setPipe($event)\"\n displayProperty=\"configurationType\"\n >\n @let patternStr = pattern();\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]=\"patternStr\"\n ></c8y-highlight>\n </c8y-li>\n <ng-template #notFoundTemplate>\n @if (patternStr.length > 0) {\n <c8y-li class=\"bg-level-2 p-8\">\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 }\n </ng-template>\n </c8y-typeahead>\n </c8y-form-group>\n </div>\n\n <fieldset\n class=\"d-col\"\n [class.flex-grow]=\"form.value.uploadChoice === 'editInline'\"\n [class.min-height-0]=\"form.value.uploadChoice === 'editInline'\"\n >\n <legend\n class=\"legend form-block m-t-40\"\n translate\n >\n Configuration file\n </legend>\n\n @if (isExternalUrl()) {\n <p\n class=\"text-muted m-b-8\"\n translate\n >\n Content is sourced from a remote URL and cannot be edited inline.\n </p>\n <c8y-form-group>\n <label\n for=\"confFileUrl\"\n translate\n >\n File URL\n </label>\n <input\n class=\"form-control\"\n id=\"confFileUrl\"\n placeholder=\"https://...\"\n type=\"url\"\n autocomplete=\"off\"\n [ngModel]=\"binary.url\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"binary = { url: $event }; form.markAsDirty()\"\n />\n </c8y-form-group>\n } @else {\n <c8y-file-picker\n [maxAllowedFiles]=\"1\"\n [allowedUploadChoices]=\"['uploadBinary', 'uploadUrl', 'editInline']\"\n [uploadChoice]=\"form.controls.uploadChoice.value\"\n (uploadChoiceChange)=\"onUploadChoiceChange($event)\"\n [fileUrl]=\"binary.url\"\n (fileUrlChange)=\"onFileUrlChange($event)\"\n [fileBinary]=\"binary.file\"\n (fileBinaryChange)=\"onFileBinaryChange($event)\"\n (textFilePicked)=\"onTextFilePicked($event)\"\n [fileUrlPopover]=\"textForConfigurationUrlPopover\"\n >\n <ng-container c8yInlineEditor>\n <div class=\"row\">\n <c8y-form-group class=\"col-sm-9\">\n <label\n for=\"confEditorFilename\"\n translate\n >\n Filename\n </label>\n <input\n class=\"form-control\"\n id=\"confEditorFilename\"\n placeholder=\"config.json\"\n aria-required=\"true\"\n type=\"text\"\n autocomplete=\"off\"\n formControlName=\"editorFilename\"\n />\n </c8y-form-group>\n <c8y-form-group class=\"col-sm-3\">\n <label\n for=\"confEditorLanguage\"\n translate\n >\n Language\n </label>\n <c8y-select\n aria-label=\"{{ 'Editor language' | translate }}\"\n id=\"confEditorLanguage\"\n [items]=\"editorLanguages\"\n formControlName=\"editorLanguage\"\n ></c8y-select>\n </c8y-form-group>\n </div>\n <c8y-form-group class=\"d-col flex-grow min-height-0 m-b-0\">\n <label\n for=\"confEditorContent\"\n translate\n >\n Content\n </label>\n <c8y-editor\n class=\"flex-grow\"\n style=\"min-height: 95px\"\n id=\"confEditorContent\"\n formControlName=\"editorContent\"\n monacoEditorMarkerValidator\n [editorOptions]=\"{ language: editorLanguage() }\"\n ></c8y-editor>\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </ng-container>\n </c8y-file-picker>\n }\n </fieldset>\n </div>\n\n <div class=\"card-footer j-c-between separator flex-no-shrink p-24\">\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 [class.btn-pending]=\"saving()\"\n [title]=\"submitButtonTitle() | translate\"\n type=\"submit\"\n [disabled]=\"isSaveDisabled\"\n >\n {{ submitButtonTitle() | translate }}\n </button>\n </div>\n</form>\n" }]
|
|
932
|
+
}], ctorParameters: () => [], propDecorators: { version: [{
|
|
933
|
+
type: Input
|
|
934
|
+
}], description: [{
|
|
935
|
+
type: Input
|
|
936
|
+
}], binary: [{
|
|
937
|
+
type: Input
|
|
938
|
+
}], deviceType: [{
|
|
939
|
+
type: Input
|
|
940
|
+
}], mo: [{
|
|
941
|
+
type: Input
|
|
763
942
|
}] } });
|
|
764
943
|
|
|
765
944
|
class ConfigurationListComponent {
|
|
766
|
-
constructor(
|
|
767
|
-
this.alert =
|
|
768
|
-
this.gridService =
|
|
769
|
-
this.repositoryService =
|
|
770
|
-
this.
|
|
771
|
-
this.modalService =
|
|
772
|
-
this.translateService =
|
|
773
|
-
this.inventoryBinaryService =
|
|
945
|
+
constructor() {
|
|
946
|
+
this.alert = inject(AlertService);
|
|
947
|
+
this.gridService = inject(DataGridService);
|
|
948
|
+
this.repositoryService = inject(RepositoryService);
|
|
949
|
+
this.bottomDrawerService = inject(BottomDrawerService);
|
|
950
|
+
this.modalService = inject(ModalService);
|
|
951
|
+
this.translateService = inject(TranslateService);
|
|
952
|
+
this.inventoryBinaryService = inject(InventoryBinaryService);
|
|
774
953
|
this.refresh$ = new EventEmitter();
|
|
775
954
|
this.pagination = {
|
|
776
955
|
pageSize: 50,
|
|
@@ -868,43 +1047,44 @@ class ConfigurationListComponent {
|
|
|
868
1047
|
}
|
|
869
1048
|
async add() {
|
|
870
1049
|
try {
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
keyboard: false
|
|
877
|
-
}).content.result;
|
|
1050
|
+
const drawer = this.bottomDrawerService.openDrawer(ConfigurationDetailComponent, {
|
|
1051
|
+
disableClickOutside: true,
|
|
1052
|
+
closeOnNavigation: true
|
|
1053
|
+
});
|
|
1054
|
+
await drawer.instance.result;
|
|
878
1055
|
this.refresh$.next();
|
|
879
1056
|
}
|
|
880
1057
|
catch (ex) {
|
|
881
|
-
// intended empty
|
|
1058
|
+
// intended empty — user canceled or save failed (error displayed by detail component)
|
|
882
1059
|
}
|
|
883
1060
|
}
|
|
884
1061
|
async edit(configuration) {
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1062
|
+
let fileBinary = null;
|
|
1063
|
+
try {
|
|
1064
|
+
fileBinary = await this.repositoryService.getBinaryFile(configuration.url, {
|
|
1065
|
+
allowExternal: false
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
catch {
|
|
1069
|
+
// Binary unavailable — drawer still opens; editor will be empty
|
|
1070
|
+
}
|
|
888
1071
|
try {
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
ariaLabelledBy: 'configurationModalTitle',
|
|
893
|
-
ignoreBackdropClick: true,
|
|
894
|
-
keyboard: false,
|
|
1072
|
+
const drawer = this.bottomDrawerService.openDrawer(ConfigurationDetailComponent, {
|
|
1073
|
+
disableClickOutside: true,
|
|
1074
|
+
closeOnNavigation: true,
|
|
895
1075
|
initialState: {
|
|
896
1076
|
version: configuration.name,
|
|
897
1077
|
deviceType: configuration.deviceType,
|
|
898
1078
|
description: configuration.description,
|
|
899
|
-
binary: { file: fileBinary, url: configuration.url }
|
|
1079
|
+
binary: { file: fileBinary, url: configuration.url },
|
|
1080
|
+
mo: configuration
|
|
900
1081
|
}
|
|
901
|
-
})
|
|
902
|
-
|
|
903
|
-
await modal.result;
|
|
1082
|
+
});
|
|
1083
|
+
await drawer.instance.result;
|
|
904
1084
|
this.refresh$.next();
|
|
905
1085
|
}
|
|
906
1086
|
catch (ex) {
|
|
907
|
-
// intended empty
|
|
1087
|
+
// intended empty — user canceled or save failed (error displayed by detail component)
|
|
908
1088
|
}
|
|
909
1089
|
}
|
|
910
1090
|
isBinaryFile(configuration) {
|
|
@@ -948,12 +1128,12 @@ class ConfigurationListComponent {
|
|
|
948
1128
|
trackByName(_index, column) {
|
|
949
1129
|
return column.name;
|
|
950
1130
|
}
|
|
951
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ConfigurationListComponent, deps: [
|
|
952
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: ConfigurationListComponent, isStandalone: true, selector: "c8y-configuration-list",
|
|
1131
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ConfigurationListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1132
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: ConfigurationListComponent, isStandalone: true, selector: "c8y-configuration-list", ngImport: i0, template: "<c8y-title>\n <span\n class=\"m-r-4\"\n translate\n >\n Configuration repository\n </span>\n <small>\n {{ size$ | async }}\n <span translate>snapshots</span>\n </small>\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-management\"\n label=\"{{ 'Management' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n icon=\"gears\"\n label=\"{{ 'Configuration repository' | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Add configuration snapshot' | translate }}\"\n type=\"button\"\n (click)=\"add()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add configuration snapshot' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n src=\"/docs/device-management-application/managing-device-data/#managing-configurations\"\n></c8y-help>\n\n<div class=\"content-fullpage border-top border-bottom\">\n <c8y-data-grid\n [title]=\"'Configurations' | translate\"\n [refresh]=\"refresh$\"\n [pagination]=\"pagination\"\n [columns]=\"columns\"\n [actionControls]=\"actionControls\"\n [infiniteScroll]=\"'auto'\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n >\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'gears'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n >\n @if (stats?.size === 0) {\n <p>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Add configuration snapshot' | translate }}\"\n type=\"button\"\n (click)=\"add()\"\n >\n {{ 'Add configuration snapshot' | translate }}\n </button>\n </p>\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a\n c8y-guide-href=\"/docs/device-management-application/managing-device-data/#managing-configurations\"\n >\n user documentation</a\n >.\n </small>\n </p>\n }\n </c8y-ui-empty-state>\n </c8y-data-grid>\n</div>\n", dependencies: [{ kind: "component", type: TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "component", type: DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "loading", "columns", "rows", "pagination", "childNodePagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows", "treeGrid", "hideReload", "childNodesProperty", "parentNodeLabelProperty"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "directive", type: EmptyStateContextDirective, selector: "[emptyStateContext]" }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "component", type: GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "directive", type: GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
953
1133
|
}
|
|
954
1134
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ConfigurationListComponent, decorators: [{
|
|
955
1135
|
type: Component,
|
|
956
|
-
args: [{ selector: 'c8y-configuration-list', imports: [
|
|
1136
|
+
args: [{ selector: 'c8y-configuration-list', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
957
1137
|
TitleComponent,
|
|
958
1138
|
C8yTranslateDirective,
|
|
959
1139
|
BreadcrumbComponent,
|
|
@@ -969,10 +1149,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
969
1149
|
AsyncPipe,
|
|
970
1150
|
C8yTranslatePipe
|
|
971
1151
|
], template: "<c8y-title>\n <span\n class=\"m-r-4\"\n translate\n >\n Configuration repository\n </span>\n <small>\n {{ size$ | async }}\n <span translate>snapshots</span>\n </small>\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-management\"\n label=\"{{ 'Management' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n icon=\"gears\"\n label=\"{{ 'Configuration repository' | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Add configuration snapshot' | translate }}\"\n type=\"button\"\n (click)=\"add()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add configuration snapshot' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n src=\"/docs/device-management-application/managing-device-data/#managing-configurations\"\n></c8y-help>\n\n<div class=\"content-fullpage border-top border-bottom\">\n <c8y-data-grid\n [title]=\"'Configurations' | translate\"\n [refresh]=\"refresh$\"\n [pagination]=\"pagination\"\n [columns]=\"columns\"\n [actionControls]=\"actionControls\"\n [infiniteScroll]=\"'auto'\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n >\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'gears'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n >\n @if (stats?.size === 0) {\n <p>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Add configuration snapshot' | translate }}\"\n type=\"button\"\n (click)=\"add()\"\n >\n {{ 'Add configuration snapshot' | translate }}\n </button>\n </p>\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a\n c8y-guide-href=\"/docs/device-management-application/managing-device-data/#managing-configurations\"\n >\n user documentation</a\n >.\n </small>\n </p>\n }\n </c8y-ui-empty-state>\n </c8y-data-grid>\n</div>\n" }]
|
|
972
|
-
}], ctorParameters: () => [
|
|
973
|
-
type: ViewChild,
|
|
974
|
-
args: [FilterInputComponent, { static: false }]
|
|
975
|
-
}] } });
|
|
1152
|
+
}], ctorParameters: () => [] });
|
|
976
1153
|
|
|
977
1154
|
class ConfigurationRepositoryNavigationFactory {
|
|
978
1155
|
constructor() {
|
|
@@ -1043,5 +1220,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
1043
1220
|
* Generated bundle index. Do not edit.
|
|
1044
1221
|
*/
|
|
1045
1222
|
|
|
1046
|
-
export { ConfigurationDetailComponent, ConfigurationFilterPipe, ConfigurationListComponent, ConfigurationPreviewComponent, ConfigurationRepositoryDeviceTabModule, ConfigurationRepositoryListModule, ConfigurationRepositoryModule, DeviceConfigurationComponent, DeviceConfigurationGuard, DeviceConfigurationListComponent, DeviceConfigurationService, SaveToRepositoryComponent, SourceCodePreviewComponent, TextBasedConfigurationComponent };
|
|
1223
|
+
export { ConfigurationDetailComponent, ConfigurationFilterPipe, ConfigurationListComponent, ConfigurationPreviewComponent, ConfigurationRepositoryDeviceTabModule, ConfigurationRepositoryListModule, ConfigurationRepositoryModule, DeviceConfigurationComponent, DeviceConfigurationGuard, DeviceConfigurationListComponent, DeviceConfigurationService, SaveToRepositoryComponent, SourceCodePreviewComponent, TextBasedConfigurationComponent, detectLanguage };
|
|
1047
1224
|
//# sourceMappingURL=c8y-ngx-components-repository-configuration.mjs.map
|