diff --git a/application/src/main/data/json/system/widget_bundles/gateway_widgets.json b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json index dc86c0f003..c81b564960 100644 --- a/application/src/main/data/json/system/widget_bundles/gateway_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json @@ -62,6 +62,24 @@ "settingsDirective": "tb-gateway-config-single-device-widget-settings", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"gatewayTitle\":\"Gateway configuration (Single device)\"},\"title\":\"Gateway configuration (Single device)\"}" } - } + }, + { + "alias": "gateway_list", + "name": "Gateway list", + "image": null, + "description": null, + "descriptor": { + "type": "static", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": " .tb-entity-table {\r\n padding: 0 !important;\r\n }", + "controllerScript": "self.onInit = function() {\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "settingsDirective": "tb-gateway-list-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"cardHtml\":\"
HTML code here
\",\"cardCss\":\".card {\\n font-weight: bold;\\n font-size: 32px;\\n color: #999;\\n width: 100%;\\n height: 100%;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\"},\"title\":\"Gateway list\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" + }, ] } \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/gateway/gateway-command-dialog.component.html b/ui-ngx/src/app/modules/home/components/gateway/gateway-command-dialog.component.html new file mode 100644 index 0000000000..d5040b8714 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/gateway/gateway-command-dialog.component.html @@ -0,0 +1,85 @@ + +
+ +

gateway.command

+ + +
+ + +
+
+
+ gateway.linux-macos +
+
{{ linuxCode }}
+ +
+ gateway.windows +
+
{{ windowsCode }}
+ +
+
+
+
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/gateway/gateway-command-dialog.component.ts b/ui-ngx/src/app/modules/home/components/gateway/gateway-command-dialog.component.ts new file mode 100644 index 0000000000..7c742e4e01 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/gateway/gateway-command-dialog.component.ts @@ -0,0 +1,93 @@ +/// +/// ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL +/// +/// Copyright © 2016-2022 ThingsBoard, Inc. All Rights Reserved. +/// +/// NOTICE: All information contained herein is, and remains +/// the property of ThingsBoard, Inc. and its suppliers, +/// if any. The intellectual and technical concepts contained +/// herein are proprietary to ThingsBoard, Inc. +/// and its suppliers and may be covered by U.S. and Foreign Patents, +/// patents in process, and are protected by trade secret or copyright law. +/// +/// Dissemination of this information or reproduction of this material is strictly forbidden +/// unless prior written permission is obtained from COMPANY. +/// +/// Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, +/// managers or contractors who have executed Confidentiality and Non-disclosure agreements +/// explicitly covering such access. +/// +/// The copyright notice above does not evidence any actual or intended publication +/// or disclosure of this source code, which includes +/// information that is confidential and/or proprietary, and is a trade secret, of COMPANY. +/// ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, +/// OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT +/// THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, +/// AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. +/// THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION +/// DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, +/// OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. +/// + +import { Component, Inject, OnInit } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { DialogComponent } from '@app/shared/components/dialog.component'; +import { TranslateService } from '@ngx-translate/core'; +import {Device, DeviceCredentials} from "@shared/models/device.models"; +import {ActionNotificationShow} from "@core/notification/notification.actions"; + +export interface GatewayCommandDialogData { + device: Device, + credentials: DeviceCredentials +} + +@Component({ + selector: 'tb-gateway-command-dialog', + templateUrl: './gateway-command-dialog.component.html', + styleUrls: [] +}) +export class GatewayCommandDialogComponent extends DialogComponent implements OnInit { + linuxCode: string; + windowsCode: string; + + constructor(protected router: Router, + protected store: Store, + private translate: TranslateService, + @Inject(MAT_DIALOG_DATA) public data: GatewayCommandDialogData, + public dialogRef: MatDialogRef,) { + super(store, router, dialogRef); + const ACCESS_TOKEN = data.credentials.credentialsId; + this.linuxCode = "docker run -it -v ~/.tb-gateway/logs:/thingsboard_gateway/logs -v " + + "~/.tb-gateway/extensions:/thingsboard_gateway/extensions -v ~/.tb-gateway/config:/thingsboard_gateway/config --name tb-gateway -e accessToken=" + + ACCESS_TOKEN + + " --restart always thingsboard/tb-gateway"; + this.windowsCode = "docker run -it -v %HOMEPATH%/tb-gateway/config:/thingsboard_gateway/config -v " + + "%HOMEPATH%/tb-gateway/extensions:/thingsboard_gateway/extensions -v %HOMEPATH%/tb-gateway/logs:/thingsboard_gateway/logs " + + "--name tb-gateway -e host=thingsboard.cloud -p 1883 -e accessToken=" + + ACCESS_TOKEN + + " --restart always thingsboard/tb-gateway"; + + } + + ngOnInit(): void { + } + + close(): void { + this.dialogRef.close(); + } + + onDockerCodeCopied() { + this.store.dispatch(new ActionNotificationShow( + { + message: this.translate.instant('gateway.command-copied-message'), + type: 'success', + target: 'dockerCommandDialogContent', + duration: 1200, + verticalPosition: 'bottom', + horizontalPosition: 'left' + })); + } +} diff --git a/ui-ngx/src/app/modules/home/components/gateway/gateway-list-table-config.ts b/ui-ngx/src/app/modules/home/components/gateway/gateway-list-table-config.ts new file mode 100644 index 0000000000..da45dce83d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/gateway/gateway-list-table-config.ts @@ -0,0 +1,142 @@ +/// +/// ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL +/// +/// Copyright © 2016-2022 ThingsBoard, Inc. All Rights Reserved. +/// +/// NOTICE: All information contained herein is, and remains +/// the property of ThingsBoard, Inc. and its suppliers, +/// if any. The intellectual and technical concepts contained +/// herein are proprietary to ThingsBoard, Inc. +/// and its suppliers and may be covered by U.S. and Foreign Patents, +/// patents in process, and are protected by trade secret or copyright law. +/// +/// Dissemination of this information or reproduction of this material is strictly forbidden +/// unless prior written permission is obtained from COMPANY. +/// +/// Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, +/// managers or contractors who have executed Confidentiality and Non-disclosure agreements +/// explicitly covering such access. +/// +/// The copyright notice above does not evidence any actual or intended publication +/// or disclosure of this source code, which includes +/// information that is confidential and/or proprietary, and is a trade secret, of COMPANY. +/// ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, +/// OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT +/// THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, +/// AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. +/// THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION +/// DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, +/// OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. +/// + +import { + DateEntityTableColumn, + EntityTableColumn, + EntityTableConfig +} from '@home/models/entity/entities-table-config.models'; +import {EntityTypeResource} from '@shared/models/entity-type.models'; +import {TranslateService} from '@ngx-translate/core'; +import {DatePipe} from '@angular/common'; +import {Direction} from '@shared/models/page/sort-order'; +import {MatDialog} from '@angular/material/dialog'; +import {TimePageLink} from '@shared/models/page/page-link'; +import {Observable} from 'rxjs'; +import {PageData} from '@shared/models/page/page-data'; +import {UtilsService} from '@core/services/utils.service'; +import {DeviceService} from "@core/http/device.service"; +import {AttributeService} from "@core/http/attribute.service"; +import {Device} from "@shared/models/device.models"; +import { + GatewayCommandDialogComponent, + GatewayCommandDialogData +} from "@home/components/gateway/gateway-command-dialog.component"; +import {ActionNotificationShow} from "@core/notification/notification.actions"; +import {Store} from "@ngrx/store"; +import {AppState} from "@core/core.state"; +import {map} from "rxjs/operators"; + +export class GatewayListTableConfig extends EntityTableConfig { + + constructor(protected store: Store, + private deviceService: DeviceService, + private attributeService: AttributeService, + private datePipe: DatePipe, + private translate: TranslateService, + private utils: UtilsService, + private dialog: MatDialog, + updateOnInit = true, + pageMode = false) { + super(); + this.loadDataOnInit = updateOnInit; + this.tableTitle = ''; + this.useTimePageLink = false; + this.pageMode = pageMode; + this.detailsPanelEnabled = false; + this.selectionEnabled = false; + this.searchEnabled = true; + this.addEnabled = false; + this.entitiesDeleteEnabled = false; + this.actionsColumnTitle = ''; + this.entityTranslations = { + noEntities: 'gateway.no-gateway-found', + search: 'gateway.gateway-search' + }; + this.entityResources = {} as EntityTypeResource; + + this.entitiesFetchFunction = pageLink => this.fetchGateways(pageLink); + + this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; + + this.columns.push( + new DateEntityTableColumn('createdTime', 'gateway.created-time', this.datePipe, '150px')); + + this.columns.push( + new EntityTableColumn('entityName', 'gateway.gateway-name', '20%', + (entity => this.utils.customTranslation(entity.name, entity.name)) + ) + ); + + this.cellActionDescriptors.push( + { + name: this.translate.instant('gateway.command'), + icon: 'vpn_key', + isEnabled: () => true, + onAction: ($event, entity) => this.showGatewayDockerCommand(entity) + } + ); + } + + showGatewayDockerCommand(entity: Device) { + this.deviceService.getDeviceCredentials(entity.id.id).subscribe(credentials => { + this.dialog.open + (GatewayCommandDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + credentials: credentials, + device: entity + } + }).afterClosed().subscribe( + (res) => { + if (res) { + this.updateData(); + } + } + ); + }, error => { + const messageToShow = `
${error}
`; + this.store.dispatch(new ActionNotificationShow({message: messageToShow, type: 'error'})); + }) + } + + fetchGateways(pageLink: TimePageLink): Observable> { + return this.deviceService.getUserDevices(pageLink).pipe( + map(pageData => { + pageData.data = pageData.data.filter(device => device.additionalInfo.gateway); + pageData.totalElements = pageData.data.length; + return pageData; + }) + ); + } +} diff --git a/ui-ngx/src/app/modules/home/components/gateway/gateway-list.component.html b/ui-ngx/src/app/modules/home/components/gateway/gateway-list.component.html new file mode 100644 index 0000000000..35cde6d91b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/gateway/gateway-list.component.html @@ -0,0 +1,33 @@ + + diff --git a/ui-ngx/src/app/modules/home/components/gateway/gateway-list.component.scss b/ui-ngx/src/app/modules/home/components/gateway/gateway-list.component.scss new file mode 100644 index 0000000000..0ad3466e17 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/gateway/gateway-list.component.scss @@ -0,0 +1,55 @@ +/** + * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL + * + * Copyright © 2016-2022 ThingsBoard, Inc. All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of ThingsBoard, Inc. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to ThingsBoard, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * + * Dissemination of this information or reproduction of this material is strictly forbidden + * unless prior written permission is obtained from COMPANY. + * + * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, + * managers or contractors who have executed Confidentiality and Non-disclosure agreements + * explicitly covering such access. + * + * The copyright notice above does not evidence any actual or intended publication + * or disclosure of this source code, which includes + * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. + * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, + * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT + * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, + * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. + * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION + * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, + * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. + */ +:host { + width: 100%; + height: 100%; + display: block; + .tb-table-widget { + .table-container { + position: relative; + } + .mat-table { + .mat-row { + &.invisible { + visibility: hidden; + } + } + } + span.no-data-found { + position: absolute; + top: 60px; + bottom: 0; + left: 0; + right: 0; + } + } +} + diff --git a/ui-ngx/src/app/modules/home/components/gateway/gateway-list.component.ts b/ui-ngx/src/app/modules/home/components/gateway/gateway-list.component.ts new file mode 100644 index 0000000000..73214a56ec --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/gateway/gateway-list.component.ts @@ -0,0 +1,75 @@ +/// +/// ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL +/// +/// Copyright © 2016-2022 ThingsBoard, Inc. All Rights Reserved. +/// +/// NOTICE: All information contained herein is, and remains +/// the property of ThingsBoard, Inc. and its suppliers, +/// if any. The intellectual and technical concepts contained +/// herein are proprietary to ThingsBoard, Inc. +/// and its suppliers and may be covered by U.S. and Foreign Patents, +/// patents in process, and are protected by trade secret or copyright law. +/// +/// Dissemination of this information or reproduction of this material is strictly forbidden +/// unless prior written permission is obtained from COMPANY. +/// +/// Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, +/// managers or contractors who have executed Confidentiality and Non-disclosure agreements +/// explicitly covering such access. +/// +/// The copyright notice above does not evidence any actual or intended publication +/// or disclosure of this source code, which includes +/// information that is confidential and/or proprietary, and is a trade secret, of COMPANY. +/// ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, +/// OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT +/// THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, +/// AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. +/// THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION +/// DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, +/// OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. +/// + +import {Component, OnInit} from '@angular/core'; +import {UtilsService} from '@core/services/utils.service'; +import {TranslateService} from '@ngx-translate/core'; +import {DeviceService} from '@core/http/device.service'; +import {AttributeService} from '@core/http/attribute.service'; +import {GatewayListTableConfig} from "@home/components/gateway/gateway-list-table-config"; +import {DatePipe} from "@angular/common"; +import {MatDialog} from "@angular/material/dialog"; +import {Store} from "@ngrx/store"; +import {AppState} from "@core/core.state"; + +@Component({ + selector: 'tb-gateway-list', + templateUrl: './gateway-list.component.html', + styleUrls: ['./gateway-list.component.scss'] +}) + + +export class GatewayListComponent implements OnInit { + gatewayListTableConfig: GatewayListTableConfig; + + constructor( + protected store: Store, + private utils: UtilsService, + private translate: TranslateService, + private datePipe: DatePipe, + private deviceService: DeviceService, + private attributeService: AttributeService, + private dialog: MatDialog, + ) { + } + + ngOnInit(): void { + this.gatewayListTableConfig = new GatewayListTableConfig( + this.store, + this.deviceService, + this.attributeService, + this.datePipe, + this.translate, + this.utils, + this.dialog + ); + } +} diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 7cfcf269de..466967da74 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -173,6 +173,7 @@ import { RateLimitsDetailsDialogComponent } from '@home/components/profile/tenan import { AssetProfileComponent } from '@home/components/profile/asset-profile.component'; import { AssetProfileDialogComponent } from '@home/components/profile/asset-profile-dialog.component'; import { AssetProfileAutocompleteComponent } from '@home/components/profile/asset-profile-autocomplete.component'; +import { GatewayListComponent } from "@home/components/gateway/gateway-list.component"; @NgModule({ declarations: @@ -195,6 +196,7 @@ import { AssetProfileAutocompleteComponent } from '@home/components/profile/asse RelationFiltersComponent, AlarmTableHeaderComponent, AlarmTableComponent, + GatewayListComponent, AttributeTableComponent, AddAttributeDialogComponent, EditAttributeValuePanelComponent, @@ -342,6 +344,7 @@ import { AssetProfileAutocompleteComponent } from '@home/components/profile/asse RelationTableComponent, RelationFiltersComponent, AlarmTableComponent, + GatewayListComponent, AttributeTableComponent, AliasesEntitySelectComponent, AliasesEntityAutocompleteComponent, diff --git a/ui-ngx/src/app/modules/home/components/shared-home-components.module.ts b/ui-ngx/src/app/modules/home/components/shared-home-components.module.ts index 4052958e86..5ba9dc8dc5 100644 --- a/ui-ngx/src/app/modules/home/components/shared-home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/shared-home-components.module.ts @@ -18,6 +18,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@app/shared/shared.module'; import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-details-dialog.component'; +import { GatewayCommandDialogComponent } from "@home/components/gateway/gateway-command-dialog.component"; import { SHARED_HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; @NgModule({ @@ -26,14 +27,16 @@ import { SHARED_HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; ], declarations: [ - AlarmDetailsDialogComponent + AlarmDetailsDialogComponent, + GatewayCommandDialogComponent ], imports: [ CommonModule, SharedModule ], exports: [ - AlarmDetailsDialogComponent + AlarmDetailsDialogComponent, + GatewayCommandDialogComponent ] }) export class SharedHomeComponentsModule { } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 434535a785..1af245c05e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2364,6 +2364,8 @@ }, "gateway": { "add-entry": "Add configuration", + "command": "Docker commands", + "command-copied-message": "Docker command has been copied to clipboard", "connector-add": "Add new connector", "connector-enabled": "Enable connector", "connector-name": "Connector name", @@ -2371,8 +2373,10 @@ "connector-type": "Connector type", "connector-type-required": "Connector type is required.", "connectors": "Connectors configuration", + "copy-command": "Copy docker command", "create-new-gateway": "Create a new gateway", "create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?", + "created-time": "Created time", "delete": "Delete configuration", "download-tip": "Download configuration file", "gateway": "Gateway", @@ -2380,8 +2384,10 @@ "gateway-name": "Gateway name", "gateway-name-required": "Gateway name is required.", "gateway-saved": "Gateway configuration successfully saved.", + "gateway-search": "Gateway search", "json-parse": "Not valid JSON.", "json-required": "Field cannot be empty.", + "linux-macos": "Linux/MacOS", "no-connectors": "No connectors", "no-data": "No configurations", "no-gateway-found": "No gateway found.", @@ -2434,7 +2440,8 @@ "tls-path-private-key": "Path to private key on gateway", "toggle-fullscreen": "Toggle fullscreen", "transformer-json-config": "Configuration JSON*", - "update-config": "Add/update configuration JSON" + "update-config": "Add/update configuration JSON", + "windows": "Windows" }, "grid": { "delete-item-title": "Are you sure you want to delete this item?",