@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
493 lines (487 loc) • 142 kB
JavaScript
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 ===