@eohjsc/react-native-smart-city 0.4.63 → 0.4.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eohjsc/react-native-smart-city",
3
3
  "title": "React Native Smart Home",
4
- "version": "0.4.63",
4
+ "version": "0.4.64",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -1,5 +1,5 @@
1
1
  import React, { memo } from 'react';
2
- import { StyleSheet, View } from 'react-native';
2
+ import { Platform, StyleSheet, View } from 'react-native';
3
3
  import { IconOutline } from '@ant-design/icons-react-native';
4
4
  import { useTranslations } from '../../hooks/Common/useTranslations';
5
5
  import { Colors } from '../../configs';
@@ -54,6 +54,7 @@ const styles = StyleSheet.create({
54
54
  flexDirection: 'column',
55
55
  justifyContent: 'center',
56
56
  alignItems: 'center',
57
+ marginTop: Platform.OS === 'ios' ? 25 : 0,
57
58
  },
58
59
  connectStatus: {
59
60
  flexDirection: 'row',
@@ -54,7 +54,6 @@ export default StyleSheet.create({
54
54
  marginLeft: 0,
55
55
  },
56
56
  wrapTitle: {
57
- flex: 1,
58
57
  marginHorizontal: 12,
59
58
  },
60
59
  });
@@ -275,7 +275,7 @@ describe('test Item', () => {
275
275
  });
276
276
  expect(mockedNavigate).not.toBeCalled();
277
277
  expect(spyToastError).toBeCalledWith(
278
- getTranslate('en', 'reach_max_automations_per_unit'),
278
+ getTranslate('en', 'reach_max_automations_per_unit', { length: 0 }),
279
279
  '',
280
280
  7000
281
281
  );
@@ -83,6 +83,9 @@ export const watchMultiConfigs = async (configIds) => {
83
83
  };
84
84
 
85
85
  export const realWatchMultiConfigs = async (configIds) => {
86
+ if (configIds.length === 0) {
87
+ return;
88
+ }
86
89
  const { success, data } = await axiosPost(
87
90
  API.IOT.CHIP_MANAGER.WATCH_CONFIGS(),
88
91
  {
@@ -148,7 +148,7 @@ describe('Test select device type', () => {
148
148
  });
149
149
  expect(mockedNavigate).not.toBeCalled();
150
150
  expect(spyToastError).toBeCalledWith(
151
- getTranslate('en', 'reach_max_configs_per_unit'),
151
+ getTranslate('en', 'reach_max_configs_per_unit', { length: 1 }),
152
152
  '',
153
153
  7000
154
154
  );
@@ -189,7 +189,7 @@ describe('Test select device type', () => {
189
189
  });
190
190
  expect(mockedNavigate).not.toBeCalled();
191
191
  expect(spyToastError).toBeCalledWith(
192
- getTranslate('en', 'reach_max_chips_per_unit'),
192
+ getTranslate('en', 'reach_max_chips_per_unit', { length: 1 }),
193
193
  '',
194
194
  7000
195
195
  );
@@ -305,7 +305,7 @@ describe('Test ScriptDetail', () => {
305
305
  });
306
306
  expect(mockNavigate).not.toBeCalled();
307
307
  expect(spyToastError).toBeCalledWith(
308
- getTranslate('en', 'reach_max_actions_per_automation'),
308
+ getTranslate('en', 'reach_max_actions_per_automation', { length: 0 }),
309
309
  '',
310
310
  7000
311
311
  );
@@ -252,7 +252,7 @@ describe('Test MultiUnits', () => {
252
252
  });
253
253
  expect(mockedNavigate).not.toBeCalled();
254
254
  expect(spyToastError).toBeCalledWith(
255
- getTranslate('en', 'reach_max_automations_per_unit'),
255
+ getTranslate('en', 'reach_max_automations_per_unit', { length: 0 }),
256
256
  '',
257
257
  7000
258
258
  );
@@ -148,7 +148,7 @@ describe('Test Automate', () => {
148
148
  });
149
149
  expect(mockedNavigate).not.toBeCalled();
150
150
  expect(spyToastError).toBeCalledWith(
151
- getTranslate('en', 'reach_max_automations_per_unit'),
151
+ getTranslate('en', 'reach_max_automations_per_unit', { length: 0 }),
152
152
  '',
153
153
  7000
154
154
  );
@@ -495,8 +495,7 @@ const DeviceDetail = ({ route }) => {
495
495
  useFocusEffect(
496
496
  useCallback(() => {
497
497
  let params = new URLSearchParams();
498
- const configIds = [];
499
-
498
+ let configIds = [];
500
499
  // todo Bang refactor widgets like dashboard
501
500
  display.items.map((item) => {
502
501
  if (!item.configuration) {
@@ -514,9 +513,8 @@ const DeviceDetail = ({ route }) => {
514
513
  });
515
514
  if (item.configuration.config) {
516
515
  const config = item.configuration.config;
517
- config?.id &&
518
- !configIds.includes(config.id) &&
519
- configIds.push(config.id);
516
+ const configId = config?.id ? config?.id : config;
517
+ !configIds.includes(configId) && configIds.push(configId);
520
518
  }
521
519
  });
522
520
  configIdsTemp.current = configIds.filter(Boolean);
@@ -62,7 +62,7 @@ describe('useEvaluateValue', () => {
62
62
  };
63
63
  });
64
64
 
65
- test('should evaluate range correctly', () => {
65
+ test('should evaluate range correctly 1', () => {
66
66
  const value = 1.2;
67
67
  const valueEvaluation = {
68
68
  id: 8662,
@@ -70,8 +70,56 @@ describe('useEvaluateValue', () => {
70
70
  configuration: {
71
71
  ranges: [
72
72
  {
73
- end: 1,
73
+ start: 0,
74
+ end: 0.9,
75
+ evaluate: {
76
+ icon: 'CheckCircleOutlined',
77
+ text: 'Active',
78
+ borderColor: '#00d084',
79
+ backgroundColor: 'rgba(0, 208, 132, 0.3)',
80
+ },
81
+ end_condition: '<',
82
+ start_condition: '>=',
83
+ },
84
+ {
74
85
  start: 1,
86
+ end: 2,
87
+ evaluate: {
88
+ icon: 'CheckCircleOutlined',
89
+ text: 'InActive',
90
+ borderColor: '#abb8c3',
91
+ backgroundColor: 'rgba(171, 184, 195, 0.3)',
92
+ },
93
+ end_condition: '<',
94
+ start_condition: '>=',
95
+ },
96
+ ],
97
+ },
98
+ configs: [28265],
99
+ };
100
+
101
+ const { result } = renderHook(() => useEvaluateValue());
102
+ const evaluateValue = result.current;
103
+
104
+ const evaluatedResult = evaluateValue(value, valueEvaluation);
105
+ expect(evaluatedResult).toEqual({
106
+ backgroundColor: 'rgba(171, 184, 195, 0.3)',
107
+ borderColor: '#abb8c3',
108
+ icon: 'CheckCircleOutlined',
109
+ text: 'InActive',
110
+ });
111
+ });
112
+
113
+ test('should evaluate range correctly 2', () => {
114
+ const value = 1.2;
115
+ const valueEvaluation = {
116
+ id: 8662,
117
+ template: 'range',
118
+ configuration: {
119
+ ranges: [
120
+ {
121
+ start: 0,
122
+ end: null,
75
123
  evaluate: {
76
124
  icon: 'CheckCircleOutlined',
77
125
  text: 'Active',
@@ -82,8 +130,56 @@ describe('useEvaluateValue', () => {
82
130
  start_condition: '>=',
83
131
  },
84
132
  {
85
- end: 0,
133
+ start: 1,
134
+ end: 2,
135
+ evaluate: {
136
+ icon: 'CheckCircleOutlined',
137
+ text: 'InActive',
138
+ borderColor: '#abb8c3',
139
+ backgroundColor: 'rgba(171, 184, 195, 0.3)',
140
+ },
141
+ end_condition: '<',
142
+ start_condition: '>=',
143
+ },
144
+ ],
145
+ },
146
+ configs: [28265],
147
+ };
148
+
149
+ const { result } = renderHook(() => useEvaluateValue());
150
+ const evaluateValue = result.current;
151
+
152
+ const evaluatedResult = evaluateValue(value, valueEvaluation);
153
+ expect(evaluatedResult).toEqual({
154
+ icon: 'CheckCircleOutlined',
155
+ text: 'Active',
156
+ borderColor: '#00d084',
157
+ backgroundColor: 'rgba(0, 208, 132, 0.3)',
158
+ });
159
+ });
160
+
161
+ test('should evaluate range correctly 3', () => {
162
+ const value = 1.2;
163
+ const valueEvaluation = {
164
+ id: 8662,
165
+ template: 'range',
166
+ configuration: {
167
+ ranges: [
168
+ {
86
169
  start: 0,
170
+ end: 0.9,
171
+ evaluate: {
172
+ icon: 'CheckCircleOutlined',
173
+ text: 'Active',
174
+ borderColor: '#00d084',
175
+ backgroundColor: 'rgba(0, 208, 132, 0.3)',
176
+ },
177
+ end_condition: '<',
178
+ start_condition: '>=',
179
+ },
180
+ {
181
+ start: 1,
182
+ end: null,
87
183
  evaluate: {
88
184
  icon: 'CheckCircleOutlined',
89
185
  text: 'InActive',
@@ -110,23 +206,60 @@ describe('useEvaluateValue', () => {
110
206
  });
111
207
  });
112
208
 
209
+ test('should evaluate map correctly', () => {
210
+ const value = 0;
211
+ const valueEvaluation = {
212
+ id: 8662,
213
+ template: 'map',
214
+ configuration: {
215
+ maps: [
216
+ {
217
+ evaluate: {
218
+ icon: 'CheckCircleOutlined',
219
+ text: 'Active',
220
+ borderColor: '#00d084',
221
+ backgroundColor: 'rgba(0, 208, 132, 0.3)',
222
+ },
223
+ values: [1],
224
+ },
225
+ {
226
+ evaluate: {
227
+ icon: 'CheckCircleOutlined',
228
+ text: 'InActive',
229
+ borderColor: '#abb8c3',
230
+ backgroundColor: 'rgba(171, 184, 195, 0.3)',
231
+ },
232
+ values: [0],
233
+ },
234
+ ],
235
+ },
236
+ configs: [28265],
237
+ };
238
+
239
+ const { result } = renderHook(() => useEvaluateValue());
240
+ const evaluateValue = result.current;
241
+
242
+ const evaluatedResult = evaluateValue(value, valueEvaluation);
243
+ expect(evaluatedResult).toEqual({
244
+ backgroundColor: 'rgba(171, 184, 195, 0.3)',
245
+ borderColor: '#abb8c3',
246
+ icon: 'CheckCircleOutlined',
247
+ text: 'InActive',
248
+ });
249
+ });
250
+
113
251
  test('should evaluate boolean correctly', () => {
114
252
  const value = 0;
115
253
  const valueEvaluation = {
116
254
  id: 8662,
117
255
  template: 'boolean',
256
+ is_on_value: [1],
118
257
  configuration: {
119
258
  on: {
120
- value: 1,
121
- evaluate: {
122
- text: 'On',
123
- },
259
+ text: 'On',
124
260
  },
125
261
  off: {
126
- value: 0,
127
- evaluate: {
128
- text: 'Off',
129
- },
262
+ text: 'Off',
130
263
  },
131
264
  },
132
265
  configs: [28265],
@@ -20,49 +20,82 @@ const evaluateRange = (value, configuration) => {
20
20
 
21
21
  for (let i = 0; i < configuration?.ranges?.length; i++) {
22
22
  const range = configuration.ranges[i];
23
- if (range.start <= value && value <= range.end) {
23
+ if (range.end === null && range.start <= value) {
24
+ return range.evaluate;
25
+ }
26
+ if (range.start === null && range.end >= value) {
24
27
  return range.evaluate;
25
28
  }
26
- if (!range.end && range.start <= value) {
29
+ if (range.start <= value && value <= range.end) {
27
30
  return range.evaluate;
28
31
  }
29
32
  }
30
33
  return { text: value };
31
34
  };
32
35
 
36
+ const isNumberEqual = (v1, v2) => {
37
+ return parseFloat(v1) === parseFloat(v2);
38
+ };
39
+
40
+ const isContainNumber = (values, v) => {
41
+ return values.find((value) => isNumberEqual(value, v)) !== undefined;
42
+ };
43
+
44
+ const evaluateMap = (value, configuration) => {
45
+ /*
46
+ configuration: {
47
+ maps: [
48
+ { values: [1], evaluate: 'On' },
49
+ { values: [0], evaluate: {text: 'Off'} },
50
+ ]
51
+ }
52
+ */
53
+
54
+ if (!value) {
55
+ value = 0;
56
+ }
57
+
58
+ for (let i = 0; i < configuration?.maps?.length; i++) {
59
+ const map = configuration.maps[i];
60
+ if (isContainNumber(map.values, value)) {
61
+ return map.evaluate;
62
+ }
63
+ }
64
+ return { text: value?.toString() };
65
+ };
66
+
33
67
  const evaluateBoolean = (value, configuration) => {
34
68
  /*
35
69
  configuration: {
70
+ 'is_on_value': [1],
36
71
  'on': {
37
- 'value': 1,
38
- 'evaluate': {
39
- 'text': 'On',
40
- },
72
+ 'text': 'On',
41
73
  },
42
74
  'off': {
43
- 'value': 0,
44
-
45
- 'evaluate': {
46
- 'text': 'Off',
47
- },
75
+ 'text': 'Off',
48
76
  },
49
77
  }
50
78
  */
51
79
  if (!value) {
52
80
  value = 0;
53
81
  }
54
- if (value === configuration.on?.value) {
55
- return configuration.on.evaluate;
82
+ let isOn =
83
+ configuration.is_on_value?.find((v) => isNumberEqual(v, value)) !==
84
+ undefined;
85
+ if (configuration.is_on_value) {
86
+ isOn = value;
56
87
  }
57
- if (value === configuration.off?.value) {
58
- return configuration.off.evaluate;
88
+
89
+ if (isOn) {
90
+ return configuration.on;
59
91
  }
60
- return { text: value };
92
+ return configuration.off;
61
93
  };
62
94
 
63
95
  export const valueEvaluationFuncs = {
64
96
  range: evaluateRange,
65
97
  boolean: evaluateBoolean,
98
+ map: evaluateMap,
66
99
  };
67
100
 
68
101
  export const useEvaluateValue = () => {
@@ -101,7 +101,7 @@ describe('test MemberList', () => {
101
101
  });
102
102
  expect(mockedNavigate).not.toBeCalled();
103
103
  expect(spyToastError).toBeCalledWith(
104
- getTranslate('en', 'reach_max_members_per_unit'),
104
+ getTranslate('en', 'reach_max_members_per_unit', { length: 0 }),
105
105
  '',
106
106
  7000
107
107
  );
@@ -136,7 +136,13 @@ const AddSubUnit = ({ route }) => {
136
136
  if (
137
137
  unitChoose?.stations?.length >= permissions?.max_stations_per_unit
138
138
  ) {
139
- ToastBottomHelper.error(t('reach_max_stations_per_unit'), '', 7000);
139
+ ToastBottomHelper.error(
140
+ t('reach_max_stations_per_unit', {
141
+ length: permissions?.max_stations_per_unit,
142
+ }),
143
+ '',
144
+ 7000
145
+ );
140
146
  }
141
147
  }
142
148
  }
@@ -343,7 +343,7 @@ describe('Test AddSubUnit', () => {
343
343
  await viewButtonBottom.props.onRightClick();
344
344
  });
345
345
  expect(spyToastError).toBeCalledWith(
346
- getTranslate('en', 'reach_max_stations_per_unit'),
346
+ getTranslate('en', 'reach_max_stations_per_unit', { length: 0 }),
347
347
  '',
348
348
  7000
349
349
  );
@@ -6,7 +6,7 @@ describe('Test ValueEvaluation', () => {
6
6
  const { result } = renderHook(() =>
7
7
  evaluateValue(1, { template: 'boolean', configuration: { id: 1 } })
8
8
  );
9
- expect(result.current).toEqual({ text: 1 });
9
+ expect(result.current).toEqual(undefined);
10
10
  });
11
11
  it('render ValueEvaluation none configuration', async () => {
12
12
  const { result } = renderHook(() =>
@@ -14,7 +14,7 @@ describe('Test ValueEvaluation', () => {
14
14
  );
15
15
  expect(result.current).toEqual({ text: 1 });
16
16
  });
17
- it('render ValueEvaluation none valueEvalation', async () => {
17
+ it('render ValueEvaluation none valueEvaluation', async () => {
18
18
  const { result } = renderHook(() => evaluateValue(1));
19
19
  expect(result.current).toEqual({ text: 1 });
20
20
  });
@@ -164,7 +164,7 @@ describe('Test AddMenu Unit', () => {
164
164
  menuActionAddnew.props.onItemClick(menuActionAddnew.props.dataActions[0]);
165
165
  });
166
166
  expect(spyToastError).toBeCalledWith(
167
- getTranslate('en', 'reach_max_stations_per_unit'),
167
+ getTranslate('en', 'reach_max_stations_per_unit', { length: 1 }),
168
168
  '',
169
169
  7000
170
170
  );
@@ -1410,31 +1410,32 @@ export default {
1410
1410
  'minutes.\n\n Some device control functions will be interrupted during the upgrade.[Era] would like to ask ' +
1411
1411
  'for your understanding for this inconvenience.\n\nBest regards.',
1412
1412
  please_enter_a_number: 'Please enter a number',
1413
- reach_max_stations_per_unit:
1414
- 'Sub-unit is incomplete. Please visit app.e-ra.io to complete your operation.',
1415
1413
  not_support_plug_and_play: 'Not support plug and play',
1414
+ reach_max_stations_per_unit:
1415
+ 'You have reached the maximum number of {length} sub-unit per unit.',
1416
1416
  reach_max_actions_per_automation:
1417
- 'Action is incomplete. Please visit app.e-ra.io to complete your operation.',
1417
+ 'You have reached the maximum number of {length} actions per automation.',
1418
1418
  reach_max_automations_per_unit:
1419
- 'Automation is incomplete. Please visit app.e-ra.io to complete your operation.',
1419
+ 'You have reached the maximum number of {length} automations per unit.',
1420
1420
  reach_max_members_per_unit:
1421
- 'Member is incomplete. Please visit app.e-ra.io to complete your operation.',
1421
+ 'You have reached the maximum number of {length} members per unit.',
1422
1422
  reach_max_chips_per_unit:
1423
- 'Chip is incomplete. Please visit app.e-ra.io to complete your operation.',
1423
+ 'You have reached the maximum number of {length} chips per unit.',
1424
1424
  reach_max_configs_per_unit:
1425
- 'Config is incomplete. Please visit app.e-ra.io to complete your operation.',
1425
+ 'You have reached the maximum number of {length} configurations per unit.',
1426
1426
  no_permission_plug_and_play_modbus:
1427
- 'Add modbus is incomplete. Please visit app.e-ra.io to complete your operation.',
1427
+ 'You do not have permission to add Modbus devices. Please upgrade your service package at e-ra.io',
1428
1428
  no_permission_plug_and_play_zigbee:
1429
- 'Add zigbee is incomplete. Please visit app.e-ra.io to complete your operation.',
1429
+ 'You do not have permission to add Zigbee devices. Please upgrade your service package at e-ra.io',
1430
1430
  no_permission_plug_and_play_wifi:
1431
- 'Add wifi is incomplete. Please visit app.e-ra.io to complete your operation.',
1431
+ 'You do not have permission to add Wifi devices. Please upgrade your service package at e-ra.io',
1432
1432
  no_permission_plug_and_play_gateway:
1433
- 'Add gateway is incomplete. Please visit app.e-ra.io to complete your operation.',
1433
+ 'You do not have permission to add Gateways. Please upgrade your service package at e-ra.io',
1434
1434
  no_permission_view_action_log:
1435
- 'View action log is incomplete. Please visit app.e-ra.io to complete your operation.',
1435
+ 'You do not have permission to view Activity Logs. Please upgrade your service package at e-ra.io',
1436
1436
  no_permission_smart_script_for_multi_unit:
1437
- 'Smart script for Multi Unit is incomplete. Please visit app.e-ra.io to complete your operation.',
1437
+ 'You do not have permission to create additional Smart Scripts for multiple locations. ' +
1438
+ 'Please upgrade your service package at e-ra.io',
1438
1439
  you_can_only_add_more: 'You can only add more {number}',
1439
1440
  actions: 'actions',
1440
1441
  value_must_be_less_than_8_character:
@@ -1422,31 +1422,31 @@ export default {
1422
1422
  value_must_be_greater_than_min: 'Giá trị phải lớn hơn {min}',
1423
1423
  value_must_be_less_than_max: 'Giá trị phải nhỏ hơn {max}',
1424
1424
  please_enter_a_number: 'Vui lòng nhập một số',
1425
- reach_max_stations_per_unit:
1426
- 'Sub-unit chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1427
1425
  not_support_plug_and_play: 'Không hỗ trợ kết nối tự động',
1426
+ reach_max_stations_per_unit:
1427
+ 'Bạn đã đạt đến số lượng tối đa {length} khu vực cho mỗi địa điểm.',
1428
1428
  reach_max_actions_per_automation:
1429
- 'Action chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1429
+ 'Bạn đã đạt đến số lượng tối đa {length} hành động cho mỗi tự động hoá.',
1430
1430
  reach_max_automations_per_unit:
1431
- 'Automation chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1431
+ 'Bạn đã đạt đến số lượng tối đa {length} tự động hoá cho mỗi địa điểm.',
1432
1432
  reach_max_members_per_unit:
1433
- 'Member chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1433
+ 'Bạn đã đạt đến số lượng tối đa {length} thành viên cho mỗi địa điểm.',
1434
1434
  reach_max_chips_per_unit:
1435
- 'Chip chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1435
+ 'Bạn đã đạt đến số lượng tối đa {length} chip cho mỗi địa điểm.',
1436
1436
  reach_max_configs_per_unit:
1437
- 'Config chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1437
+ 'Bạn đã đạt đến số lượng tối đa {length} thông số cho mỗi địa điểm..',
1438
1438
  no_permission_plug_and_play_modbus:
1439
- 'Kết nối tự động modbus chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1439
+ 'Bạn không quyền thêm thiết bị Modbus. Vui lòng nâng cấp gói dịch vụ tại e-ra.io',
1440
1440
  no_permission_plug_and_play_zigbee:
1441
- 'Kết nối tự động zigbee chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1441
+ 'Bạn không quyền thêm thiết bị Zigbee. Vui lòng nâng cấp gói dịch vụ tại e-ra.io',
1442
1442
  no_permission_plug_and_play_wifi:
1443
- 'Kết nối tự động wifi chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1443
+ 'Bạn không quyền thêm thiết bị Wifi. Vui lòng nâng cấp gói dịch vụ tại e-ra.io',
1444
1444
  no_permission_plug_and_play_gateway:
1445
- 'Kết nối tự động gateway chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1445
+ 'Bạn không quyền thêm Gateway. Vui lòng nâng cấp gói dịch vụ tại e-ra.io',
1446
1446
  no_permission_view_action_log:
1447
- 'Xem lịch sử hoạt động chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1447
+ 'Bạn không quyền xem Nhật ký hoạt động. Vui lòng nâng cấp gói dịch vụ tại e-ra.io',
1448
1448
  no_permission_smart_script_for_multi_unit:
1449
- 'Kịch bản thông minh cho nhiều địa điểm chưa hoàn tất. Hãy truy cập app.e-ra.io để hoàn thiện thao tác của bạn.',
1449
+ 'Bạn không có quyền tạo thêm Kịch bản thông minh cho nhiều địa điểm. Vui lòng nâng cấp gói dịch vụ tại e-ra.io',
1450
1450
  you_can_only_add_more: 'Bạn chỉ có thể thêm {number}',
1451
1451
  actions: 'hành động',
1452
1452
  value_must_be_less_than_8_character: 'Giá trị phải ít hơn hoặc bằng 8 kí tự',