UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

493 lines (487 loc) 142 kB
import * as i0 from '@angular/core'; import { EventEmitter, Component, Output, Input, Injectable, ViewChild, NgModule } from '@angular/core'; import * as i2 from '@c8y/ngx-components'; import { gettext, Status, CoreModule, CommonModule, FormsModule, PopoverConfirmComponent, ViewContext, hookRoute } from '@c8y/ngx-components'; import * as i1 from '@c8y/client'; import { cloneDeep, escapeRegExp, orderBy, head, find, findIndex, get } from 'lodash-es'; import * as i3$1 from '@angular/router'; import { RouterModule } from '@angular/router'; import * as i6 from '@angular/forms'; import * as i3 from '@ngx-translate/core'; import * as i4 from 'ngx-bootstrap/modal'; import { saveAs } from 'file-saver'; import * as i5 from '@angular/common'; import { pipe } from 'rxjs'; import { map } from 'rxjs/operators'; class NoConnectionsFoundComponent { constructor() { this.onAction = new EventEmitter(); this.header = gettext('Connections'); } addConnection() { this.onAction.emit(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NoConnectionsFoundComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: NoConnectionsFoundComponent, selector: "no-connections-found", inputs: { header: "header" }, outputs: { onAction: "onAction" }, ngImport: i0, template: "<div class=\"card content-fullpage split-view--5-7\">\n <div class=\"card-header separator grid__col--fullspan\">\n <h4>{{ header | translate}}</h4>\n </div>\n <div class=\"inner-scroll split-view__list\">\n <div class=\"bg-level-1 flex-grow\">\n <div class=\"card-block large-padding\">\n <c8y-ui-empty-state\n [icon]=\"'plug'\"\n [title]=\"'No connections found.' | translate\"\n [subtitle]=\"'Click below to add a new connection.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n </div>\n\n <div class=\"card-footer separator\">\n <button\n title=\"{{ 'Add connection' | translate }}\"\n class=\"btn btn-primary\"\n (click)=\"addConnection()\"\n >\n <i [c8yIcon]=\"'plus-circle'\"></i>\n {{ 'Add connection' | translate }}\n </button>\n </div>\n </div>\n <div class=\"inner-scroll split-view__detail\">\n <div class=\"flex-grow\">\n <div class=\"card-block large-padding\">\n <c8y-ui-empty-state\n [icon]=\"'more-details'\"\n [title]=\"'No settings to display.' | translate\"\n [subtitle]=\"'Add a connection.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "component", type: i2.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NoConnectionsFoundComponent, decorators: [{ type: Component, args: [{ selector: 'no-connections-found', template: "<div class=\"card content-fullpage split-view--5-7\">\n <div class=\"card-header separator grid__col--fullspan\">\n <h4>{{ header | translate}}</h4>\n </div>\n <div class=\"inner-scroll split-view__list\">\n <div class=\"bg-level-1 flex-grow\">\n <div class=\"card-block large-padding\">\n <c8y-ui-empty-state\n [icon]=\"'plug'\"\n [title]=\"'No connections found.' | translate\"\n [subtitle]=\"'Click below to add a new connection.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n </div>\n\n <div class=\"card-footer separator\">\n <button\n title=\"{{ 'Add connection' | translate }}\"\n class=\"btn btn-primary\"\n (click)=\"addConnection()\"\n >\n <i [c8yIcon]=\"'plus-circle'\"></i>\n {{ 'Add connection' | translate }}\n </button>\n </div>\n </div>\n <div class=\"inner-scroll split-view__detail\">\n <div class=\"flex-grow\">\n <div class=\"card-block large-padding\">\n <c8y-ui-empty-state\n [icon]=\"'more-details'\"\n [title]=\"'No settings to display.' | translate\"\n [subtitle]=\"'Add a connection.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n </div>\n </div>\n</div>\n" }] }], propDecorators: { onAction: [{ type: Output }], header: [{ type: Input }] } }); var ConnectionType; (function (ConnectionType) { ConnectionType[ConnectionType["ACTILITY"] = 0] = "ACTILITY"; ConnectionType[ConnectionType["SIGFOX"] = 1] = "SIGFOX"; ConnectionType[ConnectionType["LORIOT"] = 2] = "LORIOT"; })(ConnectionType || (ConnectionType = {})); function isSigfoxConnection(connection) { return typeof connection.parentGroupId !== 'undefined'; } function isActilityConnection(connection) { return typeof connection.profileId !== 'undefined'; } function isLoriotConnection(connection) { return typeof connection.providerType !== 'undefined'; } class MultipleLnsConnectorService { constructor(client, appStateService, alertService) { this.client = client; this.appStateService = appStateService; this.alertService = alertService; this.headers = { 'Content-Type': 'application/json' }; } async list(connectionType) { const url = `${this.getBaseUrlByType(connectionType)}/lns-connection`; const options = { method: 'GET', headers: this.headers }; return this.client.fetch(url, options); } /** * Saves the connection. * @param connection The connection to be saved. * @param originalName The original name of the connection, required to perform an update. */ async save(connection, originalName = null) { if (originalName) { return this.update(connection, originalName); } return this.create(connection); } async detail(connectionType, connectionName) { const name = connectionName.toLocaleLowerCase(); const url = `${this.getBaseUrlByType(connectionType)}/lns-connection/${encodeURIComponent(String(name))}`; const options = { method: 'GET', headers: this.headers }; const res = await this.client.fetch(url, options); if (res.status === 200) { return await res.json(); } return null; } async exists(connectionType, connectionName) { const connection = await this.detail(connectionType, connectionName); return connection !== null; } async create(connection) { connection.name = connection.name.toLocaleLowerCase(); const url = `${this.getBaseUrlByConnection(connection)}/lns-connection`; const options = { method: 'POST', headers: this.headers, body: JSON.stringify(connection) }; return this.client.fetch(url, options); } async update(connection, originalName) { connection.name = connection.name.toLocaleLowerCase(); const url = `${this.getBaseUrlByConnection(connection)}/lns-connection/${encodeURIComponent(String(originalName))}`; const options = { method: 'PUT', headers: this.headers, body: JSON.stringify(connection) }; return this.client.fetch(url, options); } getBaseUrlByConnection(connection) { return isSigfoxConnection(connection) ? 'service/sigfox-agent' : isActilityConnection(connection) ? 'service/actility' : isLoriotConnection(connection) ? 'service/loriot' : ''; } getBaseUrlByType(connectionType) { return connectionType === ConnectionType.SIGFOX ? 'service/sigfox-agent' : connectionType === ConnectionType.ACTILITY ? 'service/actility' : connectionType === ConnectionType.LORIOT ? 'service/loriot' : ''; } async delete(connection) { const url = `${this.getBaseUrlByConnection(connection)}/lns-connection`; const options = { method: 'DELETE' }; return this.client.fetch(`${url}/${encodeURIComponent(String(connection.name))}`, options); } getApplication(name) { const { references } = this.appStateService.currentTenant.value.applications; return references.find(({ application }) => application.name === name).application; } async download(url) { try { const options = { method: 'GET' }; return this.client.fetch(url, options); } catch (e) { this.alertService.addServerFailure(e); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MultipleLnsConnectorService, deps: [{ token: i1.FetchClient }, { token: i2.AppStateService }, { token: i2.AlertService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MultipleLnsConnectorService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MultipleLnsConnectorService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.FetchClient }, { type: i2.AppStateService }, { type: i2.AlertService }] }); class ConnectionInfoWithDownloadCsvComponent { constructor(modal, connectorService, alertService, translateService) { this.modal = modal; this.connectorService = connectorService; this.alertService = alertService; this.translateService = translateService; } dismiss() { this.modal.hide(); } async download() { const url = `/service/${this.appData.contextPath}${this.messageData.attrs.URL}`; const res = await this.connectorService.download(url); if (res && res.status === 200) { const streamData = await res.blob(); saveAs(streamData, this.translateService.instant(gettext('{{ connectionName }} - devices.csv'), { connectionName: this.connectionName })); } else { this.alertService.danger(gettext('A server error occurred.')); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ConnectionInfoWithDownloadCsvComponent, deps: [{ token: i4.BsModalRef }, { token: MultipleLnsConnectorService }, { token: i2.AlertService }, { token: i3.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: ConnectionInfoWithDownloadCsvComponent, selector: "connection-info-with-download-csv", inputs: { messageData: "messageData", appData: "appData", modalTitle: "modalTitle", connectionName: "connectionName" }, ngImport: i0, template: "<div class=\"modal-dialog\">\n <div class=\"modal-content\">\n <div class=\"c8y-prompt alert alert-danger\">\n <h3 class=\"m-b-16\">\n <i class=\"dlt-c8y-icon-exclamation-circle\"></i>\n <span>{{ modalTitle | translate }}</span>\n </h3>\n <p class=\"text-break-word\">\n {{ messageData.message | translate }}\n </p>\n <br>\n <span class=\"btn-default\" (click)=\"download()\">{{\n 'Click the link to download the file with the affected devices.' | translate\n }}</span>\n <div class=\"alert-footer\">\n <button\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n class=\"btn btn-default\"\n (click)=\"dismiss()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n </div>\n </div>\n </div>\n</div>", dependencies: [{ kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ConnectionInfoWithDownloadCsvComponent, decorators: [{ type: Component, args: [{ selector: 'connection-info-with-download-csv', template: "<div class=\"modal-dialog\">\n <div class=\"modal-content\">\n <div class=\"c8y-prompt alert alert-danger\">\n <h3 class=\"m-b-16\">\n <i class=\"dlt-c8y-icon-exclamation-circle\"></i>\n <span>{{ modalTitle | translate }}</span>\n </h3>\n <p class=\"text-break-word\">\n {{ messageData.message | translate }}\n </p>\n <br>\n <span class=\"btn-default\" (click)=\"download()\">{{\n 'Click the link to download the file with the affected devices.' | translate\n }}</span>\n <div class=\"alert-footer\">\n <button\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n class=\"btn btn-default\"\n (click)=\"dismiss()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n </div>\n </div>\n </div>\n</div>" }] }], ctorParameters: () => [{ type: i4.BsModalRef }, { type: MultipleLnsConnectorService }, { type: i2.AlertService }, { type: i3.TranslateService }], propDecorators: { messageData: [{ type: Input }], appData: [{ type: Input }], modalTitle: [{ type: Input }], connectionName: [{ type: Input }] } }); class SigfoxMultipleLnsConnectorComponent { constructor(connectorService, alertService, translateService, modalService, modal) { this.connectorService = connectorService; this.alertService = alertService; this.translateService = translateService; this.modalService = modalService; this.modal = modal; this.state = 'loadingConnection'; this.cloneDeep = cloneDeep; this.connections = Array(); this.showPassword = false; this.cardHeader = gettext('Sigfox connections'); this.allowedSpecialCharacters = '~!@$^(){}[]|:,<+=,.`_ -'; this.namePattern = `^[a-zA-Z0-9 ${escapeRegExp(this.allowedSpecialCharacters)}]*$`; this.namePatternError = this.translateService.instant(gettext('Connection name can only contain letters, numbers, spaces, and the following symbols: {{ symbols }}'), { symbols: this.allowedSpecialCharacters }); } async ngOnInit() { await this.loadConnections(); } async loadConnections() { const res = await this.connectorService.list(ConnectionType.SIGFOX); if (res && res.status !== 200) { const data = res.json ? await res.json() : undefined; this.alertService.addServerFailure({ data, res }); this.state = 'loadingError'; } else { const list = await res.json(); this.connections = orderBy(list, ['name'], ['asc']); await this.setModel(); } } async setModel(connectionObj = null) { const resetConnection = await this.resetEditedUnsavedConnection(); this.connection = connectionObj ? connectionObj : this.state === 'savedSuccessfully' ? this.connection : resetConnection ?? cloneDeep(head(this.connections)); this.state = 'updateConnection'; this.showPassword = false; this.originalConnection = this.connection ? cloneDeep(this.connection) : undefined; } async resetEditedUnsavedConnection() { if (this.state !== 'updateConnection' || !this.originalConnection) { return; } const { name } = this.originalConnection; const originalData = find(this.connections, { name }); if (originalData) { return cloneDeep(originalData); } } async addConnection() { await this.resetEditedUnsavedConnection(); this.connection = {}; this.originalConnection = {}; this.state = 'addConnections'; this.showPassword = true; } async save() { const checkForConnectionName = this.originalConnection && this.originalConnection.name && this.originalConnection.name !== '' ? this.originalConnection.name : this.connection.name; const isConnectionExist = await this.connectorService.exists(ConnectionType.SIGFOX, checkForConnectionName); if (this.state === 'addConnections' && isConnectionExist) { const mesg = this.translateService.instant(gettext(`Connection with name "{{ name }}" already exists.`), { name: this.connection.name }); this.alertService.danger(mesg); } else { return this.saveConnection(); } } async deleteConnection(originalConnection) { const { name } = originalConnection; const mesg = this.translateService.instant(gettext(`You are about to delete the connection "{{ name }}". Do you want to proceed?`), { name }); try { await this.modal.confirm(gettext('Delete connection'), mesg, Status.DANGER, { ok: gettext('Delete'), cancel: gettext('Cancel') }); await this.delete(originalConnection); } catch (error) { // empty catch block } } changePassword() { this.showPassword = !this.showPassword; if (this.connectorsForm.controls.password) { this.connectorsForm.controls.password.setValue(null); } } async saveConnection() { const res = await this.connectorService.save(this.connection, this.originalConnection?.name); if (res && (res.status === 201 || res.status === 200)) { this.state = 'savedSuccessfully'; this.alertService.success(gettext('Connection saved.')); await this.loadConnections(); } else if (res && res.status === 500) { const data = res.json ? await res.json() : undefined; const app = this.connectorService.getApplication('sigfox-agent'); const initialState = { messageData: data, appData: app, modalTitle: gettext('Failed to update the connection'), ariaDescribedby: 'modal-body', ariaLabelledBy: 'modal-title', connectionName: this.connection.name }; this.modalService.show(ConnectionInfoWithDownloadCsvComponent, { initialState }); } else { const data = res.json ? await res.json() : undefined; this.alertService.addServerFailure({ data, res }); } } async delete(originalConnection) { try { const response = await this.connectorService.delete(originalConnection); if (response.ok && response.status === 204) { this.alertService.success(gettext('Connection deleted.')); await this.loadConnections(); } else if (response && response.status === 500) { const data = response.json ? await response.json() : undefined; const app = this.connectorService.getApplication('sigfox-agent'); const initialState = { messageData: data, appData: app, ariaDescribedby: 'modal-body', ariaLabelledBy: 'modal-title', modalTitle: gettext('Failed to delete the connection'), connectionName: originalConnection.name }; this.modalService.show(ConnectionInfoWithDownloadCsvComponent, { initialState }); } else { const data = response.json ? await response.json() : undefined; this.alertService.addServerFailure({ data, response }); } } catch (error) { // empty catch block } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SigfoxMultipleLnsConnectorComponent, deps: [{ token: MultipleLnsConnectorService }, { token: i2.AlertService }, { token: i3.TranslateService }, { token: i4.BsModalService }, { token: i2.ModalService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SigfoxMultipleLnsConnectorComponent, selector: "sigfox-multiple-lns-connector", viewQueries: [{ propertyName: "connectorsForm", first: true, predicate: ["connectorsForm"], descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"state === 'loadingConnection'; else renderListAndForm\">\n <c8y-loading></c8y-loading>\n</ng-container>\n<c8y-title>{{ 'Connectivity' | translate }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'cog'\"\n [label]=\"'Settings' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'cog'\"\n [label]=\"'Connectivity' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'cog'\"\n [label]=\"'Sigfox' | translate\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<ng-template #renderListAndForm>\n <no-connections-found\n (onAction)=\"addConnection()\"\n *ngIf=\"connections.length === 0 && state !== 'addConnections'\"\n [header]=\"cardHeader | translate\"\n ></no-connections-found>\n <div>\n <div\n class=\"card content-fullpage split-view--5-7\"\n *ngIf=\"connections.length !== 0 || state === 'addConnections'\"\n >\n <div class=\"card-header separator grid__col--fullspan\">\n <div class=\"card-title\">{{ cardHeader | translate }}</div>\n </div>\n <div class=\"inner-scroll split-view__list\">\n <div class=\"bg-level-1 flex-grow\">\n <c8y-list-group class=\"nav c8y-nav-stacked\">\n <c8y-li\n class=\"c8y-stacked-item p-0\"\n [class.active]=\"connection.name === connectionBeingEdited\"\n *ngFor=\"let connection of connections; let index = index\"\n (click)=\"setModel(cloneDeep(connection))\"\n >\n <c8y-li-icon [icon]=\"'plug'\"></c8y-li-icon>\n <span title=\"{{ connection.name }}\">\n {{ connection.name }}\n </span>\n </c8y-li>\n\n <c8y-li\n class=\"c8y-nav-stacked active\"\n *ngIf=\"state === 'addConnections'\"\n (click)=\"addConnection()\"\n >\n <c8y-li-icon [icon]=\"'plug'\"></c8y-li-icon>\n {{ 'New connection' | translate }}\n </c8y-li>\n </c8y-list-group>\n </div>\n <div class=\"card-footer separator-top\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Add connection' | translate }}\"\n [disabled]=\"state === 'addConnections'\"\n (click)=\"addConnection()\"\n >\n <i [c8yIcon]=\"'plus-circle'\"></i>\n {{ 'Add connection' | translate }}\n </button>\n </div>\n </div>\n\n <!-- 'split-view__detail--selected' condition needs to be fixed. this is needed so that both columns are visible in tablet format -->\n\n <div\n class=\"inner-scroll split-view__detail\"\n ng-class=\"{ 'split-view__detail--selected': vm.selected && vm.jsonSchemaObjects }\"\n >\n <div class=\"card-header separator visible-sm visible-xs fit-w sticky-top\">\n <button\n class=\"btn btn-clean text-primary\"\n title=\"{{ 'Back' | translate }}\"\n ng-click=\"vm.deselect()\"\n >\n <i [c8yIcon]=\"'chevron-left'\"></i>\n <span>{{ 'Back' | translate }}</span>\n </button>\n </div>\n <form\n class=\"d-contents\"\n #connectorsForm=\"ngForm\"\n >\n <div class=\"flex-grow\">\n <div class=\"card-block large-padding\">\n <c8y-form-group>\n <label for=\"name\">\n {{ 'Name' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"name\"\n name=\"name\"\n type=\"text\"\n required\n [placeholder]=\"'e.g. Sigfox connection' | translate\"\n [(ngModel)]=\"connection.name\"\n [pattern]=\"namePattern\"\n />\n <c8y-messages>\n <c8y-message\n name=\"pattern\"\n [text]=\"namePatternError\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"description\">\n {{ 'Description' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"description\"\n name=\"description\"\n type=\"text\"\n [placeholder]=\"\n 'e.g. This connection has a built-in functionality to\u2026' | translate\n \"\n [(ngModel)]=\"connection.description\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"baseUrl\">\n {{ 'URL' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"baseUrl\"\n name=\"baseUrl\"\n type=\"text\"\n required\n [placeholder]=\"\n 'e.g. {{ example }}' | translate : { example: 'https://backend.sigfox.com/api' }\n \"\n [(ngModel)]=\"connection.baseUrl\"\n />\n </c8y-form-group>\n <c8y-form-group>\n <label for=\"parentGroupId\">\n {{ 'Parent group ID' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"parentGroupId\"\n name=\"parentGroupId\"\n type=\"text\"\n required\n [placeholder]=\"\n 'e.g. {{ example }}' | translate : { example: '58c1793b9e93a15370f71caa' }\n \"\n [(ngModel)]=\"connection.parentGroupId\"\n pattern=\"[a-z\\d]+\"\n />\n <c8y-messages>\n <c8y-message\n name=\"pattern\"\n text=\"{{ 'Must be a valid Parent group ID' | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"username\">\n {{ 'Username' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"username\"\n placeholder=\"{{ 'e.g. joe`LOCALIZE`' | translate }}\"\n name=\"username\"\n type=\"text\"\n required\n [(ngModel)]=\"connection.username\"\n />\n </c8y-form-group>\n <c8y-form-group *ngIf=\"showPassword\">\n <label for=\"password\">\n {{ 'Password' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"password\"\n placeholder=\"{{ 'e.g. my_password' | translate }}\"\n name=\"password\"\n type=\"password\"\n required\n [(ngModel)]=\"connection.password\"\n />\n </c8y-form-group>\n\n <button\n class=\"btn btn-default\"\n name=\"changePassword\"\n type=\"button\"\n *ngIf=\"state === 'updateConnection'\"\n (click)=\"changePassword()\"\n >\n <span\n title=\"{{ 'Change password' | translate }}\"\n *ngIf=\"!showPassword\"\n >\n {{ 'Change password' | translate }}\n </span>\n <span\n title=\"{{ 'Cancel password change' | translate }}\"\n *ngIf=\"showPassword\"\n >\n {{ 'Cancel password change' | translate }}\n </span>\n </button>\n </div>\n </div>\n\n <div class=\"card-footer separator-top\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"setModel()\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-danger\"\n title=\"{{ 'Delete' | translate }}\"\n type=\"button\"\n data-cy=\"sigfox-multiple-lns-connector.component--delete-connectivity\"\n *ngIf=\"state === 'updateConnection'\"\n (click)=\"deleteConnection(originalConnection)\"\n translate\n >\n Delete\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n data-cy=\"sigfox-multiple-lns-connector.component--save-connectivity\"\n type=\"submit\"\n [disabled]=\"!this.connectorsForm.form.valid || this.connectorsForm.form.pristine\"\n (click)=\"save()\"\n translate\n >\n Save\n </button>\n </div>\n </form>\n </div>\n </div>\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: i2.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i2.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: i2.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: i6.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i6.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: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i6.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i6.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i6.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i2.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: i2.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i2.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i2.ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: NoConnectionsFoundComponent, selector: "no-connections-found", inputs: ["header"], outputs: ["onAction"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SigfoxMultipleLnsConnectorComponent, decorators: [{ type: Component, args: [{ selector: 'sigfox-multiple-lns-connector', template: "<ng-container *ngIf=\"state === 'loadingConnection'; else renderListAndForm\">\n <c8y-loading></c8y-loading>\n</ng-container>\n<c8y-title>{{ 'Connectivity' | translate }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'cog'\"\n [label]=\"'Settings' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'cog'\"\n [label]=\"'Connectivity' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'cog'\"\n [label]=\"'Sigfox' | translate\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<ng-template #renderListAndForm>\n <no-connections-found\n (onAction)=\"addConnection()\"\n *ngIf=\"connections.length === 0 && state !== 'addConnections'\"\n [header]=\"cardHeader | translate\"\n ></no-connections-found>\n <div>\n <div\n class=\"card content-fullpage split-view--5-7\"\n *ngIf=\"connections.length !== 0 || state === 'addConnections'\"\n >\n <div class=\"card-header separator grid__col--fullspan\">\n <div class=\"card-title\">{{ cardHeader | translate }}</div>\n </div>\n <div class=\"inner-scroll split-view__list\">\n <div class=\"bg-level-1 flex-grow\">\n <c8y-list-group class=\"nav c8y-nav-stacked\">\n <c8y-li\n class=\"c8y-stacked-item p-0\"\n [class.active]=\"connection.name === connectionBeingEdited\"\n *ngFor=\"let connection of connections; let index = index\"\n (click)=\"setModel(cloneDeep(connection))\"\n >\n <c8y-li-icon [icon]=\"'plug'\"></c8y-li-icon>\n <span title=\"{{ connection.name }}\">\n {{ connection.name }}\n </span>\n </c8y-li>\n\n <c8y-li\n class=\"c8y-nav-stacked active\"\n *ngIf=\"state === 'addConnections'\"\n (click)=\"addConnection()\"\n >\n <c8y-li-icon [icon]=\"'plug'\"></c8y-li-icon>\n {{ 'New connection' | translate }}\n </c8y-li>\n </c8y-list-group>\n </div>\n <div class=\"card-footer separator-top\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Add connection' | translate }}\"\n [disabled]=\"state === 'addConnections'\"\n (click)=\"addConnection()\"\n >\n <i [c8yIcon]=\"'plus-circle'\"></i>\n {{ 'Add connection' | translate }}\n </button>\n </div>\n </div>\n\n <!-- 'split-view__detail--selected' condition needs to be fixed. this is needed so that both columns are visible in tablet format -->\n\n <div\n class=\"inner-scroll split-view__detail\"\n ng-class=\"{ 'split-view__detail--selected': vm.selected && vm.jsonSchemaObjects }\"\n >\n <div class=\"card-header separator visible-sm visible-xs fit-w sticky-top\">\n <button\n class=\"btn btn-clean text-primary\"\n title=\"{{ 'Back' | translate }}\"\n ng-click=\"vm.deselect()\"\n >\n <i [c8yIcon]=\"'chevron-left'\"></i>\n <span>{{ 'Back' | translate }}</span>\n </button>\n </div>\n <form\n class=\"d-contents\"\n #connectorsForm=\"ngForm\"\n >\n <div class=\"flex-grow\">\n <div class=\"card-block large-padding\">\n <c8y-form-group>\n <label for=\"name\">\n {{ 'Name' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"name\"\n name=\"name\"\n type=\"text\"\n required\n [placeholder]=\"'e.g. Sigfox connection' | translate\"\n [(ngModel)]=\"connection.name\"\n [pattern]=\"namePattern\"\n />\n <c8y-messages>\n <c8y-message\n name=\"pattern\"\n [text]=\"namePatternError\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"description\">\n {{ 'Description' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"description\"\n name=\"description\"\n type=\"text\"\n [placeholder]=\"\n 'e.g. This connection has a built-in functionality to\u2026' | translate\n \"\n [(ngModel)]=\"connection.description\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"baseUrl\">\n {{ 'URL' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"baseUrl\"\n name=\"baseUrl\"\n type=\"text\"\n required\n [placeholder]=\"\n 'e.g. {{ example }}' | translate : { example: 'https://backend.sigfox.com/api' }\n \"\n [(ngModel)]=\"connection.baseUrl\"\n />\n </c8y-form-group>\n <c8y-form-group>\n <label for=\"parentGroupId\">\n {{ 'Parent group ID' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"parentGroupId\"\n name=\"parentGroupId\"\n type=\"text\"\n required\n [placeholder]=\"\n 'e.g. {{ example }}' | translate : { example: '58c1793b9e93a15370f71caa' }\n \"\n [(ngModel)]=\"connection.parentGroupId\"\n pattern=\"[a-z\\d]+\"\n />\n <c8y-messages>\n <c8y-message\n name=\"pattern\"\n text=\"{{ 'Must be a valid Parent group ID' | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"username\">\n {{ 'Username' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"username\"\n placeholder=\"{{ 'e.g. joe`LOCALIZE`' | translate }}\"\n name=\"username\"\n type=\"text\"\n required\n [(ngModel)]=\"connection.username\"\n />\n </c8y-form-group>\n <c8y-form-group *ngIf=\"showPassword\">\n <label for=\"password\">\n {{ 'Password' | translate }}\n </label>\n <input\n class=\"form-control\"\n id=\"password\"\n placeholder=\"{{ 'e.g. my_password' | translate }}\"\n name=\"password\"\n type=\"password\"\n required\n [(ngModel)]=\"connection.password\"\n />\n </c8y-form-group>\n\n <button\n class=\"btn btn-default\"\n name=\"changePassword\"\n type=\"button\"\n *ngIf=\"state === 'updateConnection'\"\n (click)=\"changePassword()\"\n >\n <span\n title=\"{{ 'Change password' | translate }}\"\n *ngIf=\"!showPassword\"\n >\n {{ 'Change password' | translate }}\n </span>\n <span\n title=\"{{ 'Cancel password change' | translate }}\"\n *ngIf=\"showPassword\"\n >\n {{ 'Cancel password change' | translate }}\n </span>\n </button>\n </div>\n </div>\n\n <div class=\"card-footer separator-top\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"setModel()\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-danger\"\n title=\"{{ 'Delete' | translate }}\"\n type=\"button\"\n data-cy=\"sigfox-multiple-lns-connector.component--delete-connectivity\"\n *ngIf=\"state === 'updateConnection'\"\n (click)=\"deleteConnection(originalConnection)\"\n translate\n >\n Delete\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n data-cy=\"sigfox-multiple-lns-connector.component--save-connectivity\"\n type=\"submit\"\n [disabled]=\"!this.connectorsForm.form.valid || this.connectorsForm.form.pristine\"\n (click)=\"save()\"\n translate\n >\n Save\n </button>\n </div>\n </form>\n </div>\n </div>\n </div>\n</ng-template>\n" }] }], ctorParameters: () => [{ type: MultipleLnsConnectorService }, { type: i2.AlertService }, { type: i3.TranslateService }, { type: i4.BsModalService }, { type: i2.ModalService }], propDecorators: { connectorsForm: [{ type: ViewChild, args: ['connectorsForm', { static: false }] }] } }); class ActilityMultipleLnsConnectorComponent { constructor(connectorService, alertService, translateService, modal, modalService) { this.connectorService = connectorService; this.alertService = alertService; this.translateService = translateService; this.modal = modal; this.modalService = modalService; this.state = 'loadingConnection'; this.connections = Array(); this.showPassword = false; this.cardHeader = gettext('Actility connections'); this.allowedSpecialCharacters = '~!@$^(){}[]|:,<+=,.`_ -'; this.namePattern = `^[a-zA-Z0-9 ${escapeRegExp(this.allowedSpecialCharacters)}]*$`; this.namePatternError = this.translateService.instant(gettext('Connection name can only contain letters, numbers, spaces, and the following symbols: {{ symbols }}'), { symbols: this.allowedSpecialCharacters }); } async ngOnInit() { await this.loadConnections(); } async loadConnections() { const res = await this.connectorService.list(ConnectionType.ACTILITY); if (res && res.status !== 200) { const data = res.json ? await res.json() : undefined; this.alertService.addServerFailure({ data, res }); this.state = 'loadingError'; } else { const list = await res.json(); this.connections = orderBy(list, ['name'], ['asc']); await this.setModel(); } } async setModel(connectionObj = null) { await this.resetEditedUnsavedConnection(); this.connection = connectionObj ? connectionObj : this.state === 'savedSuccessfully' ? this.connection : head(this.connections); this.state = 'updateConnection'; this.showPassword = false; this.originalConnection = this.connection ? cloneDeep(this.connection) : undefined; } async setAdminAndCoreApiVersion() { this.connection.adminApiVersion = 'latest'; this.connection.coreApiVersion = 'latest'; } async resetEditedUnsavedConnection() { if (this.state !== 'updateConnection' || !this.originalConnection) { return; } const { name } = this.originalConnection; const index = findIndex(this.connections, { name }); if (index !== -1) { const originalData = (await this.connectorService.detail(ConnectionType.ACTILITY, name)); this.connections[index] = originalData; } } async addConnection() { await this.resetEditedUnsavedConnection(); this.connection = {}; this.originalConnection = {}; this.state = 'addConnections'; this.showPassword = true; await this.setAdminAndCoreApiVersion(); } async save() { const checkForConnectionName = this.originalConnection && this.originalConnection.name && this.originalConnection.name !== '' ? this.originalConnection.name : this.connection.name; const isConnectionExist = await this.connectorService.exists(ConnectionType.ACTILITY, checkForConnectionName); if (this.state === 'addConnections' && isConnectionExist) { const mesg = this.translateService.instant(gettext(`Connection with name "{{ name }}" already exists.`), { name: this.connection.name }); this.alertService.danger(mesg); } else { return this.saveConnection(); } } async deleteConnection(originalConnection) { const { name } = originalConnection; const mesg = this.translateService.instant(gettext(`You are about to delete the connection "{{ name }}". Do you want to proceed?`), { name }); try { await this.modal.confirm(gettext('Delete connection'), mesg, Status.DANGER, { ok: gettext('Delete'), cancel: gettext('Cancel') }); await this.delete(originalConnection); } catch (error) { // empty catch block } } changePassword() { this.showPassword = !this.showPassword; if (this.connectorsForm.controls.password) { this.connectorsForm.controls.password.setValue(null); } } async saveConnection() { const res = await this.connectorService.save(this.connection, this.originalConnection?.name); if (res && (res.status === 201 || res.status === 200)) { this.state = 'savedSuccessfully'; this.alertService.success(gettext('Connection saved.')); await this.loadConnections(); } else if (res && res.status === 500) { const data = res.json ? await res.json() : undefined; const app = this.connectorService.getApplication('actility'); const initialState = { messageData: data, appData: app, modalTitle: gettext('Failed to update the connection'), ariaDescribedby: 'modal-body', ariaLabelledBy: 'modal-title', connectionName: this.connection.name }; this.modalService.show(ConnectionInfoWithDownloadCsvComponent, { initialState }); } else { const data = res.json ? await res.json() : undefined; this.alertService.addServerFailure({ data, res }); } } async delete(originalConnection) { try { const response = await this.connectorService.delete(originalConnection); if (response.ok && response.status ===