diff --git a/ui/src/app/api/attribute.service.js b/ui/src/app/api/attribute.service.js
index 7f235c9d89..6ed2921824 100644
--- a/ui/src/app/api/attribute.service.js
+++ b/ui/src/app/api/attribute.service.js
@@ -238,23 +238,23 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
function success() {
deferred.resolve(response.data);
},
- function fail(response) {
- deferred.reject(response);
+ function fail() {
+ deferred.reject();
}
)
} else {
deferred.resolve(response.data);
}
- }, function fail(response) {
- deferred.reject(response);
+ }, function fail() {
+ deferred.reject();
});
} else if (deleteEntityAttributesPromise) {
deleteEntityAttributesPromise.then(
function success() {
deferred.resolve();
},
- function fail(response) {
- deferred.reject(response);
+ function fail() {
+ deferred.reject();
}
)
} else {
@@ -287,23 +287,23 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
function success() {
deferred.resolve(response.data);
},
- function fail(response) {
- deferred.reject(response);
+ function fail() {
+ deferred.reject();
}
)
} else {
deferred.resolve(response.data);
}
- }, function fail(response) {
- deferred.reject(response);
+ }, function fail() {
+ deferred.reject();
});
} else if (deleteEntityTimeseriesPromise) {
deleteEntityTimeseriesPromise.then(
function success() {
deferred.resolve();
},
- function fail(response) {
- deferred.reject(response);
+ function fail() {
+ deferred.reject();
}
)
} else {
@@ -325,8 +325,8 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope + '?keys=' + keys;
$http.delete(url, config).then(function success() {
deferred.resolve();
- }, function fail(response) {
- deferred.reject(response);
+ }, function fail() {
+ deferred.reject();
});
return deferred.promise;
}
@@ -344,8 +344,8 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/delete' + '?keys=' + keys;
$http.delete(url, config).then(function success() {
deferred.resolve();
- }, function fail(response) {
- deferred.reject(response);
+ }, function fail() {
+ deferred.reject();
});
return deferred.promise;
}
diff --git a/ui/src/app/api/device.service.js b/ui/src/app/api/device.service.js
index c99376e17f..e3f9630193 100644
--- a/ui/src/app/api/device.service.js
+++ b/ui/src/app/api/device.service.js
@@ -43,8 +43,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
sendOneWayRpcCommand: sendOneWayRpcCommand,
sendTwoWayRpcCommand: sendTwoWayRpcCommand,
findByQuery: findByQuery,
- getDeviceTypes: getDeviceTypes,
- findByName: findByName
+ getDeviceTypes: getDeviceTypes
}
return service;
@@ -167,8 +166,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
var url = '/api/device';
$http.post(url, device, config).then(function success(response) {
deferred.resolve(response.data);
- }, function fail(response) {
- deferred.reject(response);
+ }, function fail() {
+ deferred.reject();
});
return deferred.promise;
}
@@ -178,18 +177,36 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
let attributesType = Object.keys(types.attributesScope);
let allPromise = [];
let promise = "";
+ let statisticalInfo = {};
for (let i = 0; i < attributesType.length; i++) {
- if (deviceRelation.attributes[attributesType[i]] && deviceRelation.attributes[attributesType[i]].length !== 0) {
- promise = attributeService.saveEntityAttributes(types.entityType.device, deviceId, types.attributesScope[attributesType[i]].value, deviceRelation.attributes[attributesType[i]], config);
+ let attrribute = attributesType[i];
+ if (deviceRelation.attributes[attrribute] && deviceRelation.attributes[attrribute].length !== 0) {
+ promise = attributeService.saveEntityAttributes(types.entityType.device, deviceId, types.attributesScope[attrribute].value, deviceRelation.attributes[attrribute], config).then(function () {
+ statisticalInfo.create = {
+ [attrribute]: deviceRelation.attributes[attributesType[i]].length
+ };
+ }, function () {
+ statisticalInfo.error = {
+ [attrribute]: deviceRelation.attributes[attributesType[i]].length
+ };
+ });
allPromise.push(promise);
}
}
if (deviceRelation.timeseries.length !== 0) {
- promise = attributeService.saveEntityTimeseries(types.entityType.device, deviceId, "time", deviceRelation.timeseries, config);
+ promise = attributeService.saveEntityTimeseries(types.entityType.device, deviceId, "time", deviceRelation.timeseries, config).then(function () {
+ statisticalInfo.create = {
+ timeseries: deviceRelation.timeseries.length
+ };
+ }, function () {
+ statisticalInfo.error = {
+ timeseries: deviceRelation.timeseries.length
+ };
+ });
allPromise.push(promise);
}
$q.all(allPromise).then(function success() {
- deferred.resolve();
+ deferred.resolve(statisticalInfo);
});
return deferred.promise;
}
@@ -197,26 +214,28 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
function saveDeviceParameters(deviceParameters, update, config) {
config = config || {};
const deferred = $q.defer();
- let statisticalInfo = {
- create: {},
- update: {},
- error: {}
- };
+ let statisticalInfo = {};
let newDevice = {
name: deviceParameters.name,
type: deviceParameters.type
};
saveDevice(newDevice, config).then(function success(response) {
- statisticalInfo.create.device = 1;
- saveDeviceRelarion(response.id.id, deviceParameters, config).then(function success() {
+ statisticalInfo.create={
+ device: 1
+ };
+ saveDeviceRelarion(response.id.id, deviceParameters, config).then(function success(response) {
+ angular.merge(statisticalInfo, response, statisticalInfo);
deferred.resolve(statisticalInfo);
});
- }, function fail(response) {
- console.log(response); // eslint-disable-line
+ }, function fail() {
if (update) {
findByName(deviceParameters.name, config).then(function success(response) {
- statisticalInfo.update.device = 1;
- saveDeviceRelarion(response.id.id, deviceParameters, config).then(function success() {
+ statisticalInfo.update = {
+ device: 1
+ };
+ saveDeviceRelarion(response.id.id, deviceParameters, config).then(function success(response) {
+ delete Object.assign(response, {update: response.create}).create;
+ angular.merge(statisticalInfo, response);
deferred.resolve(statisticalInfo);
});
}, function fail() {
@@ -384,8 +403,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
var url = '/api/tenant/devices?deviceName=' + deviceName;
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
- }, function fail(response) {
- deferred.reject(response);
+ }, function fail() {
+ deferred.reject();
});
return deferred.promise;
}
diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index 08d16b40c9..46bf2e9f14 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -352,6 +352,10 @@ export default angular.module('thingsboard.types', [])
},
entityGroup: {
columnType: {
+ clientAttribute: {
+ name: 'entity-group.column-type.client-attribute',
+ value: 'CLIENT_ATTRIBUTE'
+ },
sharedAttribute: {
name: 'entity-group.column-type.shared-attribute',
value: 'SHARED_ATTRIBUTE'
diff --git a/ui/src/app/device/device.controller.js b/ui/src/app/device/device.controller.js
index 16670fe768..ea6ed0e7d5 100644
--- a/ui/src/app/device/device.controller.js
+++ b/ui/src/app/device/device.controller.js
@@ -68,7 +68,7 @@ export function DeviceController($rootScope, userService, deviceService, custome
},
{
onAction: function ($event) {
- importExport.importDevices($event).then(
+ importExport.importDevices($event, types.entityType.device).then(
function() {
vm.grid.refreshList();
}
@@ -107,7 +107,6 @@ export function DeviceController($rootScope, userService, deviceService, custome
addItemTemplateUrl: addDeviceTemplate,
- // addItemText: function() { return $translate.instant('device.add-device-text') },
noItemsText: function() { return $translate.instant('device.no-devices-text') },
itemDetailsText: function() { return $translate.instant('device.device-details') },
isDetailsReadOnly: isCustomerUser,
@@ -338,6 +337,7 @@ export function DeviceController($rootScope, userService, deviceService, custome
icon: "add"
};
+ vm.deviceGridConfig.addItemActions = [];
} else if (vm.devicesScope === 'customer_user') {
deviceActionsList.push(
@@ -352,6 +352,7 @@ export function DeviceController($rootScope, userService, deviceService, custome
);
vm.deviceGridConfig.addItemAction = {};
+ vm.deviceGridConfig.addItemActions = [];
}
}
diff --git a/ui/src/app/import-export/import-dialog-csv.controller.js b/ui/src/app/import-export/import-dialog-csv.controller.js
index a2e4732006..18b42910be 100644
--- a/ui/src/app/import-export/import-dialog-csv.controller.js
+++ b/ui/src/app/import-export/import-dialog-csv.controller.js
@@ -16,7 +16,7 @@
import './import-dialog.scss';
/*@ngInject*/
-export default function ImportDialogCsvController($scope, $mdDialog, toast, importTitle, importFileLabel, importExport, types, $timeout) {
+export default function ImportDialogCsvController($scope, $mdDialog, toast, importTitle, importFileLabel, entityType, importExport, types, $timeout, $q) {
var vm = this;
@@ -27,63 +27,80 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo
vm.addDevices = addDevices;
vm.importParams = {
+ delim: ',',
isUpdate: true,
isHeader: true
};
+ vm.selectedStep = 0;
+ vm.stepProgress = 1;
+ vm.maxStep = 3;
+ vm.showBusyText = false;
+ vm.stepData = [
+ { step: 1, completed: false, optional: false, data: {} },
+ { step: 2, completed: false, optional: false, data: {} },
+ { step: 3, completed: false, optional: false, data: {} },
+ ];
+
+ vm.enableNextStep = function nextStep() {
+ //do not exceed into max step
+ if (vm.selectedStep >= vm.maxStep) {
+ return;
+ }
+ //do not increment vm.stepProgress when submitting from previously completed step
+ if (vm.selectedStep === vm.stepProgress - 1) {
+ vm.stepProgress = vm.stepProgress + 1;
+ }
+ vm.selectedStep = vm.selectedStep + 1;
+ };
+
+ vm.moveToPreviousStep = function moveToPreviousStep() {
+ if (vm.selectedStep > 0) {
+ vm.selectedStep = vm.selectedStep - 1;
+ }
+ };
+
+ vm.submitCurrentStep = function submitCurrentStep(stepData, isSkip) {
+ var deferred = $q.defer();
+ vm.showBusyText = true;
+ if (!stepData.completed && !isSkip) {
+ //simulate $http
+ $timeout(function () {
+ vm.showBusyText = false;
+ deferred.resolve({ status: 200, statusText: 'success', data: {} });
+ //move to next step when success
+ stepData.completed = true;
+ vm.enableNextStep();
+ }, 1000)
+ } else {
+ vm.showBusyText = false;
+ vm.enableNextStep();
+ }
+ };
+
vm.importTitle = importTitle;
vm.importFileLabel = importFileLabel;
+ vm.entityType = entityType;
vm.columnsParam = [];
vm.parseData = [];
- vm.entityType = types.entityType.device;
- vm.columnTypes = {};
- vm.entityField = {};
+ vm.delimiters = [{
+ key: ',',
+ value: ','
+ },{
+ key: ';',
+ value: ';'
+ },{
+ key: '|',
+ value: '|'
+ },{
+ key: '\t',
+ value: 'Tab'
+ }];
var parseData = {};
- switch (vm.entityType) {
- case types.entityType.device:
- vm.columnTypes = types.entityGroup.columnType;
- break;
- }
-
- vm.entityField.name = types.entityGroup.entityField.name;
-
- switch (vm.entityType) {
- case types.entityType.device:
- vm.entityField.type = types.entityGroup.entityField.type;
- // vm.entityField.assigned_customer = types.entityGroup.entityField.assigned_customer;
- break;
- }
-
- $scope.$watch('vm.columnsParam', function(newVal, prevVal){
- if (newVal && !angular.equals(newVal, prevVal)) {
- var isSelectName = false;
- var isSelectType = false;
- for (var i = 0; i < newVal.length; i++) {
- if (newVal[i].type === types.entityGroup.columnType.entityField.value &&
- newVal[i].key === types.entityGroup.entityField.name.value) {
- isSelectName = true;
- }
- if (newVal[i].type === types.entityGroup.columnType.entityField.value &&
- newVal[i].key === types.entityGroup.entityField.type.value) {
- isSelectType = true;
- }
- }
- $timeout(function () {
- vm.entityField.name.disable = isSelectName;
- vm.entityField.type.disable = isSelectType;
- });
- }
- }, true);
-
-
- function cancel() {
- $mdDialog.cancel();
- }
-
function fileAdded($file) {
if ($file.getExtension() === 'csv') {
var reader = new FileReader();
@@ -94,9 +111,8 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo
var importCSV = event.target.result;
if (importCSV && importCSV.length > 0) {
try {
- vm.importData = importCSV;
+ parseCSV(importCSV);
vm.fileName = $file.name;
- parseCSVData(vm.importData);
} catch (err) {
vm.fileName = null;
toast.showError(err.message);
@@ -109,7 +125,7 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo
}
}
- function parseCSVData(importData) {
+ function parseCSV(importData) {
var columnParam = {};
var config = {
delim: vm.importParams.delim,
@@ -202,10 +218,14 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo
function clearFile() {
$scope.theForm.$setDirty();
vm.fileName = null;
- vm.importData = null;
+ parseData = null;
vm.columnsParam = [];
}
+ function cancel() {
+ $mdDialog.cancel();
+ }
+
function importFromJson() {
$scope.theForm.$setPristine();
$mdDialog.hide(vm.importData);
diff --git a/ui/src/app/import-export/import-dialog-csv.tpl.html b/ui/src/app/import-export/import-dialog-csv.tpl.html
index a275c5c7e4..5f6cbbfcb4 100644
--- a/ui/src/app/import-export/import-dialog-csv.tpl.html
+++ b/ui/src/app/import-export/import-dialog-csv.tpl.html
@@ -63,66 +63,26 @@
-
-
-
+
+
+
+
+ {{delimiter.value}}
+
+
- Use first line is header
+ First line is header
- Update parameter device
+ Update parameters
-
-
-
-
+
diff --git a/ui/src/app/import-export/import-export.service.js b/ui/src/app/import-export/import-export.service.js
index 7820532de0..dbb1ea148b 100644
--- a/ui/src/app/import-export/import-export.service.js
+++ b/ui/src/app/import-export/import-export.service.js
@@ -578,9 +578,9 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
return deferred.promise;
}
- function importDevices($event) {
+ function importDevices($event, entityType) {
var deferred = $q.defer();
- openImportDialogCSV($event, 'device.import', 'device.device-file').then(
+ openImportDialogCSV($event, entityType,'device.import', 'device.device-file').then(
function success() {
// if (!validateImportedDashboard(dashboard)) {
// toast.showError($translate.instant('dashboard.invalid-dashboard-file-error'));
@@ -783,10 +783,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
return deferred.promise;
}
- /**
- * splitCSV function (c) 2009 Brian Huisman, see http://www.greywyvern.com/?post=258
- * Works by spliting on seperators first, then patching together quoted values
- */
function splitCSV(str, sep) {
for (var foo = str.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) {
if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) == '"') {
@@ -805,7 +801,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
return !isNaN(parseFloat(str)) && isFinite(str);
}
- function parseStringToFormatJS(str) {
+ function convertStringToJSType(str) {
if (isNumeric(str.replace(',', '.'))) {
return parseFloat(str.replace(',', '.'));
}
@@ -827,7 +823,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
let csvlines = csvdata.split(/[\r\n]+/);
let csvheaders = splitCSV(csvlines[0], delim);
if (csvheaders.length < 2) {
- toast.showError('A file should contain at least two columns');
+ toast.showError($translate.instant('entity.import-csv-number-columns-error'));
return -1;
}
let csvrows = header ? csvlines.slice(1, csvlines.length) : csvlines;
@@ -844,34 +840,47 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
let rowitems = splitCSV(row, delim);
if (rowitems.length !== result.headers.length) {
- toast.showError('Invalid file format. Row:' + (header ? result.rows.length + 2: result.rows.length + 1));
+ toast.showError($translate.instant('entity.import-csv-invalid-format-error', {line: (header ? result.rows.length + 2: result.rows.length + 1)}));
return -1;
}
for (let i = 0; i < rowitems.length; i++) {
- rowitems[i] = parseStringToFormatJS(rowitems[i]);
+ rowitems[i] = convertStringToJSType(rowitems[i]);
}
result.rows.push(rowitems);
}
}
return result;
}
+
+ function sumObject(obj1, obj2){
+ Object.keys(obj2).map(function(key) {
+ if (angular.isObject(obj2[key])) {
+ obj1[key] = obj1[key] || {};
+ angular.merge(obj1[key], sumObject(obj1[key], obj2[key]));
+ } else {
+ obj1[key] = (obj1[key] || 0) + obj2[key];
+ }
+ });
+ return obj1;
+ }
function createMultiEntity(arrayData, entityType, update, config) {
- var deferred = $q.defer();
- var allPromise = [];
+ let deferred = $q.defer();
+ let allPromise = [];
+ let statisticalInfo = {};
switch (entityType) {
case types.entityType.device:
- for(var i = 0; i < arrayData.length; i++){
- var promise = deviceService.saveDeviceParameters(arrayData[i], update, config);
+ for(let i = 0; i < arrayData.length; i++){
+ const promise = deviceService.saveDeviceParameters(arrayData[i], update, config);
allPromise.push(promise);
}
break;
}
- $q.all(allPromise).then(function success() {
- deferred.resolve();
- $timeout(function () {
- console.log("1"); // eslint-disable-line
- }, 1000);
+ $q.all(allPromise).then(function success(response) {
+ for (let i = 0; i < response.length; i++){
+ statisticalInfo = sumObject(statisticalInfo, response[i]);
+ }
+ deferred.resolve(statisticalInfo);
});
return deferred.promise;
}
@@ -950,7 +959,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
return deferred.promise;
}
- function openImportDialogCSV($event, importTitle, importFileLabel) {
+ function openImportDialogCSV($event, entityType, importTitle, importFileLabel) {
var deferred = $q.defer();
$mdDialog.show({
controller: 'ImportDialogCSVController',
@@ -958,7 +967,8 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
templateUrl: importDialogCSVTemplate,
locals: {
importTitle: importTitle,
- importFileLabel: importFileLabel
+ importFileLabel: importFileLabel,
+ entityType: entityType
},
parent: angular.element($document[0].body),
multiple: true,
diff --git a/ui/src/app/import-export/index.js b/ui/src/app/import-export/index.js
index 54ad30e1a6..114ac98d48 100644
--- a/ui/src/app/import-export/index.js
+++ b/ui/src/app/import-export/index.js
@@ -16,10 +16,12 @@
import ImportExport from './import-export.service';
import ImportDialogController from './import-dialog.controller';
import ImportDialogCSVController from './import-dialog-csv.controller';
+import TableColumnsAssignment from './table-columns-assignment.directive';
export default angular.module('thingsboard.importexport', [])
.factory('importExport', ImportExport)
.controller('ImportDialogController', ImportDialogController)
.controller('ImportDialogCSVController', ImportDialogCSVController)
+ .directive('tbTableColumnsAssignment', TableColumnsAssignment)
.name;
diff --git a/ui/src/app/import-export/table-columns-assignment.directive.js b/ui/src/app/import-export/table-columns-assignment.directive.js
new file mode 100644
index 0000000000..368d4d3a90
--- /dev/null
+++ b/ui/src/app/import-export/table-columns-assignment.directive.js
@@ -0,0 +1,91 @@
+/*
+ * Copyright © 2016-2019 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// import './timeinterval.scss';
+
+/* eslint-disable import/no-unresolved, import/default */
+
+import tableColumnsAssignment from './table-columns-assignment.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function TableColumnsAssignment() {
+ return {
+ restrict: "E",
+ scope: true,
+ bindToController: {
+ theForm: '=?',
+ columns: '=',
+ entityType: '=',
+ },
+ templateUrl: tableColumnsAssignment,
+ controller: TableColumnsAssignmentController,
+ controllerAs: 'vm'
+ };
+}
+
+/*@ngInject*/
+function TableColumnsAssignmentController($scope, types, $timeout) {
+ var vm = this;
+
+ vm.columnTypes = {};
+ vm.entityField = {};
+
+ switch (vm.entityType) {
+ case types.entityType.device:
+ vm.columnTypes.sharedAttribute = types.entityGroup.columnType.sharedAttribute;
+ vm.columnTypes.serverAttribute = types.entityGroup.columnType.serverAttribute;
+ vm.columnTypes.timeseries = types.entityGroup.columnType.timeseries;
+ vm.columnTypes.entityField = types.entityGroup.columnType.entityField;
+ break;
+ }
+
+ vm.entityField.name = types.entityGroup.entityField.name;
+
+ switch (vm.entityType) {
+ case types.entityType.device:
+ vm.entityField.type = types.entityGroup.entityField.type;
+ // vm.entityField.assigned_customer = types.entityGroup.entityField.assigned_customer;
+ break;
+ }
+
+ $scope.$watch('vm.columns', function(newVal, prevVal){
+ if (newVal && !angular.equals(newVal, prevVal)) {
+ var isSelectName = false;
+ var isSelectType = false;
+ for (var i = 0; i < newVal.length; i++) {
+ if (newVal[i].type === types.entityGroup.columnType.entityField.value &&
+ newVal[i].key === types.entityGroup.entityField.name.value) {
+ isSelectName = true;
+ }
+ if (newVal[i].type === types.entityGroup.columnType.entityField.value &&
+ newVal[i].key === types.entityGroup.entityField.type.value) {
+ isSelectType = true;
+ }
+ }
+ $timeout(function () {
+ vm.entityField.name.disable = isSelectName;
+ vm.entityField.type.disable = isSelectType;
+ });
+ }
+ }, true);
+
+ $scope.$watch('vm.columns', function(newVal, prevVal) {
+ if (vm.isEdit && !angular.equals(newVal, prevVal)) {
+ vm.theForm.$setDirty();
+ }
+ }, true);
+}
diff --git a/ui/src/app/import-export/table-columns-assignment.tpl.html b/ui/src/app/import-export/table-columns-assignment.tpl.html
new file mode 100644
index 0000000000..0cc0df5e6c
--- /dev/null
+++ b/ui/src/app/import-export/table-columns-assignment.tpl.html
@@ -0,0 +1,62 @@
+
+
+
+
+
diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json
index ba82eb8e13..c9b239f8c6 100644
--- a/ui/src/app/locale/locale.constant-en_US.json
+++ b/ui/src/app/locale/locale.constant-en_US.json
@@ -776,13 +776,16 @@
"details": "Entity details",
"no-entities-prompt": "No entities found",
"no-data": "No data to display",
- "columns-to-display": "Columns to Display"
+ "columns-to-display": "Columns to Display",
+ "import-csv-number-columns-error": "A file should contain at least two columns",
+ "import-csv-invalid-format-error": "Invalid file format. Line: '{{line}}'"
},
"entity-group": {
"column-value": "Value",
"column-title": "Title",
"column-type": {
"column-type": "Column type",
+ "client-attribute": "Client attribute",
"shared-attribute": "Shared attribute",
"server-attribute": "Server attribute",
"timeseries": "Timeseries",