@eohjsc/react-native-smart-city 0.2.61 → 0.2.65

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.
Files changed (62) hide show
  1. package/README.md +115 -68
  2. package/package.json +2 -2
  3. package/src/commons/ActionGroup/CurtainButtonTemplate.js +10 -2
  4. package/src/commons/ActionGroup/__test__/CurtainButtonTemplate.test.js +1 -1
  5. package/src/commons/ActionGroup/__test__/MenuActionAddSchedule.test.js +71 -0
  6. package/src/commons/ActionGroup/hooks/AccessScheduleDetailStyles.js +41 -0
  7. package/src/commons/ActionGroup/hooks/MenuActionAddSchedule.js +110 -0
  8. package/src/commons/ActionGroup/hooks/MenuActionAddScheduleStyle.js +69 -0
  9. package/src/commons/ActionGroup/hooks/RecurringDetail.js +97 -0
  10. package/src/commons/DateTimeRangeChange/DateTimeButton.js +6 -1
  11. package/src/commons/Device/HistoryChart.js +2 -1
  12. package/src/commons/Device/HorizontalBarChart.js +2 -1
  13. package/src/commons/Device/LinearChart.js +28 -1
  14. package/src/commons/Form/CurrencyInput.js +1 -0
  15. package/src/commons/SubUnit/OneTap/OneTapStyles.js +20 -1
  16. package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +151 -40
  17. package/src/commons/SubUnit/OneTap/index.js +64 -12
  18. package/src/configs/Constants.js +9 -0
  19. package/src/iot/RemoteControl/Bluetooth.js +6 -3
  20. package/src/iot/RemoteControl/GoogleHome.js +5 -2
  21. package/src/iot/RemoteControl/Internet.js +1 -0
  22. package/src/iot/RemoteControl/LG.js +2 -1
  23. package/src/iot/RemoteControl/index.js +13 -6
  24. package/src/navigations/SharedStack.js +8 -4
  25. package/src/navigations/UnitStack.js +10 -2
  26. package/src/screens/ActivityLog/hooks/index.js +1 -1
  27. package/src/screens/AddNewAction/SelectSensorDevices.js +18 -11
  28. package/src/screens/AddNewAction/Styles/SelectSensorDevicesStyles.js +5 -1
  29. package/src/screens/AddNewAction/__test__/SelectSensorDevices.test.js +6 -1
  30. package/src/screens/AddNewGateway/AddNewGatewayStyles.js +6 -1
  31. package/src/screens/AllCamera/index.js +27 -5
  32. package/src/screens/Automate/MultiUnits.js +7 -4
  33. package/src/screens/Automate/__test__/MultiUnits.test.js +1 -1
  34. package/src/screens/Automate/__test__/index.test.js +12 -0
  35. package/src/screens/ConfirmUnitDeletion/__test__/ConfirmUnitDeletion.test.js +61 -0
  36. package/src/screens/ConfirmUnitDeletion/index.js +64 -0
  37. package/src/screens/ConfirmUnitDeletion/styles.js +37 -0
  38. package/src/screens/EmergencySetting/index.js +2 -0
  39. package/src/screens/EmergencySetting/styles.js +8 -2
  40. package/src/screens/ScriptDetail/Styles/indexStyles.js +3 -0
  41. package/src/screens/ScriptDetail/index.js +7 -1
  42. package/src/screens/SubUnit/AddSubUnit.js +13 -2
  43. package/src/screens/SubUnit/EditSubUnit.js +8 -1
  44. package/src/screens/SubUnit/EditSubUnitStyles.js +2 -3
  45. package/src/screens/SubUnit/__test__/AddSubUnit.test.js +2 -1
  46. package/src/screens/TDSGuide/index.js +1 -1
  47. package/src/screens/Unit/Detail.js +20 -7
  48. package/src/screens/Unit/ManageUnit.js +9 -12
  49. package/src/screens/Unit/SmartAccount.js +25 -41
  50. package/src/screens/Unit/SmartAccountItem.js +2 -1
  51. package/src/screens/Unit/SmartAccountStyles.js +0 -1
  52. package/src/screens/Unit/__test__/ManageUnit.test.js +0 -6
  53. package/src/screens/Unit/__test__/SmartAccount.test.js +24 -0
  54. package/src/screens/Unit/__test__/SmartAccountItem.test.js +72 -0
  55. package/src/screens/UnitSummary/components/3PPowerConsumption/index.js +2 -1
  56. package/src/screens/UnitSummary/components/PowerConsumption/index.js +1 -1
  57. package/src/screens/UnitSummary/components/Temperature/ItemTemperature/index.js +2 -2
  58. package/src/screens/WaterQualityGuide/index.js +2 -2
  59. package/src/utils/I18n/translations/en.json +15 -3
  60. package/src/utils/I18n/translations/vi.json +15 -3
  61. package/src/utils/Route/index.js +1 -0
  62. package/src/utils/Utils.js +4 -0
@@ -30,7 +30,9 @@ import { AUTOMATE_TYPE } from '../../configs/Constants';
30
30
 
31
31
  const UnitDetail = ({ route }) => {
32
32
  const t = useTranslations();
33
+
33
34
  const { unitId, unitData, isOneTap, routeName, isAddSubUnit } = route.params;
35
+
34
36
  const isFocused = useIsFocused();
35
37
  const { stateData, setAction } = useContext(SCContext);
36
38
  const { navigate } = useNavigation();
@@ -39,7 +41,7 @@ const UnitDetail = ({ route }) => {
39
41
  const [appState, setAppState] = useState(AppState.currentState);
40
42
  const [listMenuItem, setListMenuItem] = useState([]);
41
43
  const [listStation, setListStation] = useState([]);
42
- const [oneTap, setOneTap] = useState([]);
44
+ const [listAutomate, setListAutomate] = useState([]);
43
45
  const [favorites, setFavorites] = useState({
44
46
  devices: [],
45
47
  automates: [],
@@ -106,12 +108,23 @@ const UnitDetail = ({ route }) => {
106
108
  await fetchWithCache(
107
109
  API.UNIT.AUTOMATE(unitId),
108
110
  {
109
- params: { type: AUTOMATE_TYPE.ONE_TAP },
111
+ params: { type: AUTOMATE_TYPE.ALL },
110
112
  },
111
113
  (response) => {
112
114
  const { success, data } = response;
113
115
  if (success) {
114
- setOneTap([...data]);
116
+ setListAutomate([
117
+ {
118
+ text: t(AUTOMATE_TYPE.SCENARIO),
119
+ data: data.filter((item) => item.type === AUTOMATE_TYPE.ONE_TAP),
120
+ type: AUTOMATE_TYPE.ONE_TAP,
121
+ },
122
+ {
123
+ text: t(AUTOMATE_TYPE.AUTOMATION),
124
+ data: data.filter((item) => item.type !== AUTOMATE_TYPE.ONE_TAP),
125
+ type: AUTOMATE_TYPE.AUTOMATION,
126
+ },
127
+ ]);
115
128
  setFavorites((prevData) => ({
116
129
  ...prevData,
117
130
  automates: data.filter((item) => item.script.is_star),
@@ -119,7 +132,7 @@ const UnitDetail = ({ route }) => {
119
132
  }
120
133
  }
121
134
  );
122
- }, [unitId]);
135
+ }, [t, unitId]);
123
136
 
124
137
  const onRefresh = useCallback(() => {
125
138
  fetchDetails();
@@ -242,13 +255,14 @@ const UnitDetail = ({ route }) => {
242
255
  };
243
256
 
244
257
  const renderDetailSubUnit = () => {
245
- if (station.isFavorites) {
258
+ if (station?.isFavorites) {
246
259
  return (
247
260
  <SubUnitFavorites
248
261
  unit={unit}
249
262
  isOwner={isOwner}
250
263
  favorites={favorites}
251
264
  wrapItemStyle={styles.wrapItemStyle}
265
+ isGGHomeConnected={isGGHomeConnected}
252
266
  />
253
267
  );
254
268
  }
@@ -264,8 +278,7 @@ const UnitDetail = ({ route }) => {
264
278
  return (
265
279
  <SubUnitAutomate
266
280
  isOwner={isOwner}
267
- type={AUTOMATE_TYPE.ONE_TAP}
268
- automates={oneTap}
281
+ listAutomate={listAutomate}
269
282
  unit={unit}
270
283
  wrapItemStyle={styles.wrapItemStyle}
271
284
  />
@@ -6,11 +6,7 @@ import { useTranslations } from '../../hooks/Common/useTranslations';
6
6
  import { Colors, API, Images } from '../../configs';
7
7
  import Routes from '../../utils/Route';
8
8
  import { ToastBottomHelper } from '../../utils/Utils';
9
- import {
10
- createFormData,
11
- axiosPatch,
12
- axiosDelete,
13
- } from '../../utils/Apis/axios';
9
+ import { createFormData, axiosPatch } from '../../utils/Apis/axios';
14
10
  import useBoolean from '../../hooks/Common/useBoolean';
15
11
  import useKeyboardAnimated from '../../hooks/Explore/useKeyboardAnimated';
16
12
 
@@ -128,13 +124,14 @@ const ManageUnit = ({ route }) => {
128
124
 
129
125
  const [showRemove, setshowRemove, setHideRemove] = useBoolean();
130
126
  const goRemove = useCallback(async () => {
131
- const { success } = await axiosDelete(API.UNIT.MANAGE_UNIT(unit.id));
132
- if (success) {
133
- setHideRemove(true);
134
- ToastBottomHelper.success(t('unit_deleted_successfully'));
135
- navigation.navigate(Routes.Dashboard);
136
- }
137
- }, [unit.id, setHideRemove, t, navigation]);
127
+ navigation.navigate(Routes.UnitStack, {
128
+ screen: Routes.ConfirmUnitDeletion,
129
+ params: {
130
+ unit,
131
+ },
132
+ });
133
+ setHideRemove(true);
134
+ }, [setHideRemove, navigation, unit]);
138
135
 
139
136
  const goToManageSubUnit = useCallback(() => {
140
137
  navigation.navigate(Routes.UnitStack, {
@@ -7,17 +7,16 @@ import React, {
7
7
  useState,
8
8
  memo,
9
9
  } from 'react';
10
- import { View } from 'react-native';
10
+ import { TouchableOpacity, View } from 'react-native';
11
11
  import styles from './SmartAccountStyles';
12
12
  import { API, Colors } from '../../configs';
13
13
  import Routes from '../../utils/Route';
14
14
  import { useNavigation } from '@react-navigation/native';
15
15
  import { axiosDelete, axiosGet } from '../../utils/Apis/axios';
16
16
  import { SmartAccountItem } from './SmartAccountItem';
17
- import { usePopover, useBoolean } from '../../hooks/Common';
18
- import { AlertAction, MenuActionMore } from '../../commons';
17
+ import { usePopover } from '../../hooks/Common';
18
+ import { MenuActionMore } from '../../commons';
19
19
  import { useTranslations } from '../../hooks/Common/useTranslations';
20
- import { useStateAlertRemove } from '../Unit/hook/useStateAlertRemove';
21
20
 
22
21
  const ListSmartAccount = ({ route }) => {
23
22
  const { unitId } = route?.params || {};
@@ -38,10 +37,6 @@ const ListSmartAccount = ({ route }) => {
38
37
  const { childRef, showingPopover, showPopoverWithRef, hidePopover } =
39
38
  usePopover();
40
39
 
41
- const [lockShowing, acquireLockShowing, releaseLockShowing] = useBoolean();
42
- const { stateAlertRemove, onShowRemoveAlert, hideAlertAction } =
43
- useStateAlertRemove();
44
-
45
40
  useEffect(() => {
46
41
  getAllSmartAccounts();
47
42
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -55,21 +50,7 @@ const ListSmartAccount = ({ route }) => {
55
50
  [showPopoverWithRef]
56
51
  );
57
52
 
58
- const onItemClick = useCallback(
59
- (item) => {
60
- if (!smartAccountRef?.current) {
61
- return;
62
- }
63
- if (item.action === 'delete') {
64
- acquireLockShowing();
65
- onShowRemoveAlert(smartAccountRef.current.brand)();
66
- }
67
- },
68
- [acquireLockShowing, onShowRemoveAlert]
69
- );
70
-
71
53
  const deleteSmartAccount = useCallback(async () => {
72
- hideAlertAction();
73
54
  if (!smartAccountRef?.current) {
74
55
  return;
75
56
  }
@@ -78,10 +59,22 @@ const ListSmartAccount = ({ route }) => {
78
59
  API.SMART_ACCOUNT.REMOVE_SMART_ACCOUNT(id)
79
60
  );
80
61
  success && getAllSmartAccounts();
81
- }, [hideAlertAction, getAllSmartAccounts]);
62
+ }, [getAllSmartAccounts]);
63
+
64
+ const onItemClick = useCallback(
65
+ (item) => {
66
+ if (!smartAccountRef?.current) {
67
+ return;
68
+ }
69
+ if (item.action === 'remove') {
70
+ deleteSmartAccount();
71
+ }
72
+ },
73
+ [deleteSmartAccount]
74
+ );
82
75
 
83
76
  const listMenuItem = useMemo(() => {
84
- return [{ action: 'delete', text: t('delete') }];
77
+ return [{ action: 'remove', text: t('remove_account') }];
85
78
  }, [t]);
86
79
 
87
80
  const gotoSmartAccountDetail = useCallback(
@@ -104,33 +97,24 @@ const ListSmartAccount = ({ route }) => {
104
97
  headerAniStyle={styles.headerAniStyle}
105
98
  >
106
99
  <View style={styles.wrapContent}>
107
- {data.map((item) => {
100
+ {data.map((item, index) => {
108
101
  return (
109
- <SmartAccountItem
110
- gotoSmartAccountDetail={gotoSmartAccountDetail}
111
- item={item}
112
- onShowMenuMore={onShowMenuMore}
113
- />
102
+ <TouchableOpacity key={index}>
103
+ <SmartAccountItem
104
+ gotoSmartAccountDetail={gotoSmartAccountDetail}
105
+ item={item}
106
+ onShowMenuMore={onShowMenuMore}
107
+ />
108
+ </TouchableOpacity>
114
109
  );
115
110
  })}
116
111
  </View>
117
- <AlertAction
118
- visible={stateAlertRemove.visible && !lockShowing}
119
- hideModal={hideAlertAction}
120
- title={stateAlertRemove.title}
121
- message={stateAlertRemove.message}
122
- leftButtonTitle={stateAlertRemove.leftButton}
123
- leftButtonClick={hideAlertAction}
124
- rightButtonTitle={stateAlertRemove.rightButton}
125
- rightButtonClick={deleteSmartAccount}
126
- />
127
112
  <MenuActionMore
128
113
  isVisible={showingPopover}
129
114
  hideMore={hidePopover}
130
115
  listMenuItem={listMenuItem}
131
116
  childRef={childRef}
132
117
  onItemClick={onItemClick}
133
- hideComplete={releaseLockShowing}
134
118
  />
135
119
  </WrapHeaderScrollable>
136
120
  </View>
@@ -23,6 +23,7 @@ export const SmartAccountItem = memo(
23
23
  <TouchableOpacity
24
24
  style={styles.wrapText}
25
25
  onPress={onPressGotoSmartAccountDetail}
26
+ testID={TESTID.SMART_ACCOUNT_ITEM}
26
27
  >
27
28
  <FImage
28
29
  source={{ uri: item?.logo }}
@@ -39,7 +40,7 @@ export const SmartAccountItem = memo(
39
40
  </View>
40
41
  </TouchableOpacity>
41
42
  <TouchableOpacity
42
- testID={TESTID.ITEM_QUICK_ACTION_PRESS}
43
+ testID={TESTID.SMART_ACCOUNT_ITEM_PRESSMORE}
43
44
  onPress={onPressMore}
44
45
  ref={buttonMoreRef}
45
46
  >
@@ -26,7 +26,6 @@ export default StyleSheet.create({
26
26
  },
27
27
  wrapText: {
28
28
  flexDirection: 'row',
29
- alignItems: 'space-around',
30
29
  },
31
30
  icon: {
32
31
  width: 48,
@@ -3,7 +3,6 @@ import { act, create } from 'react-test-renderer';
3
3
  import axios from 'axios';
4
4
 
5
5
  import { ToastBottomHelper } from '../../../utils/Utils';
6
- import API from '../../../configs/API';
7
6
  import { TESTID } from '../../../configs/Constants';
8
7
 
9
8
  import ManageUnit from '../ManageUnit';
@@ -131,7 +130,6 @@ describe('Test Manage Unit', () => {
131
130
  });
132
131
 
133
132
  test('remove Unit success', async () => {
134
- const spyToast = jest.spyOn(ToastBottomHelper, 'success');
135
133
  await act(async () => {
136
134
  tree = create(wrapComponent(route));
137
135
  });
@@ -165,11 +163,7 @@ describe('Test Manage Unit', () => {
165
163
  await bottomButton.props.onPress();
166
164
  });
167
165
 
168
- expect(axios.delete).toBeCalledWith(API.UNIT.MANAGE_UNIT(1));
169
166
  expect(mockedDispatch).not.toBeCalled();
170
- expect(spyToast).toBeCalled();
171
- spyToast.mockReset();
172
- spyToast.mockRestore();
173
167
  });
174
168
 
175
169
  test('rename Unit sucess', async () => {
@@ -6,6 +6,7 @@ import { SCProvider } from '../../../context';
6
6
  import { mockSCStore } from '../../../context/mockStore';
7
7
  import ListSmartAccount from '../SmartAccount';
8
8
  import { SmartAccountItem } from '../SmartAccountItem';
9
+ import { MenuActionMore } from '../../../commons';
9
10
 
10
11
  const wrapComponent = (route, navigation) => (
11
12
  <SCProvider initState={mockSCStore({})}>
@@ -13,6 +14,17 @@ const wrapComponent = (route, navigation) => (
13
14
  </SCProvider>
14
15
  );
15
16
 
17
+ const mockedNavigate = jest.fn();
18
+
19
+ jest.mock('@react-navigation/native', () => {
20
+ return {
21
+ ...jest.requireActual('@react-navigation/native'),
22
+ useNavigation: () => ({
23
+ goBack: mockedNavigate,
24
+ }),
25
+ };
26
+ });
27
+
16
28
  jest.mock('react', () => {
17
29
  return {
18
30
  ...jest.requireActual('react'),
@@ -55,4 +67,16 @@ describe('Test SmartAccount', () => {
55
67
  const smartAccountItem = instance.findAllByType(SmartAccountItem);
56
68
  expect(smartAccountItem.length).toEqual(3);
57
69
  });
70
+
71
+ test('test render SmartAccountItem', async () => {
72
+ await act(async () => {
73
+ tree = await renderer.create(wrapComponent());
74
+ });
75
+ const instance = tree.root;
76
+ const menuActionMore = instance.findByType(MenuActionMore);
77
+ await act(async () => {
78
+ menuActionMore.props.onItemClick();
79
+ });
80
+ expect(menuActionMore.props.isVisible).toEqual(false);
81
+ });
58
82
  });
@@ -0,0 +1,72 @@
1
+ import React from 'react';
2
+ import renderer, { act } from 'react-test-renderer';
3
+
4
+ import { SCProvider } from '../../../context';
5
+ import { mockSCStore } from '../../../context/mockStore';
6
+ import { SmartAccountItem } from '../SmartAccountItem';
7
+ import { TouchableOpacity } from 'react-native';
8
+ import { TESTID } from '../../../configs/Constants';
9
+
10
+ const mockedOnShowMenuMore = jest.fn();
11
+ const mockedGotoSmartAccountDetail = jest.fn();
12
+
13
+ const wrapComponent = (route) => (
14
+ <SCProvider initState={mockSCStore({})}>
15
+ <SmartAccountItem
16
+ onShowMenuMore={mockedOnShowMenuMore}
17
+ gotoSmartAccountDetail={mockedGotoSmartAccountDetail}
18
+ />
19
+ </SCProvider>
20
+ );
21
+
22
+ const mockedNavigate = jest.fn();
23
+
24
+ jest.mock('@react-navigation/native', () => {
25
+ return {
26
+ ...jest.requireActual('@react-navigation/native'),
27
+ useNavigation: () => ({
28
+ goBack: mockedNavigate,
29
+ }),
30
+ };
31
+ });
32
+
33
+ jest.mock('react', () => {
34
+ return {
35
+ ...jest.requireActual('react'),
36
+ memo: (x) => x,
37
+ };
38
+ });
39
+
40
+ jest.mock('axios');
41
+ describe('Test SmartAccountItem', () => {
42
+ let tree;
43
+ test('test render SmartAccountItem', async () => {
44
+ await act(async () => {
45
+ tree = await renderer.create(wrapComponent());
46
+ });
47
+ const instance = tree.root;
48
+
49
+ const onPressGotoSmartAccountDetail = instance.find(
50
+ (el) =>
51
+ el.props.testID === TESTID.SMART_ACCOUNT_ITEM &&
52
+ el.type === TouchableOpacity
53
+ );
54
+
55
+ await act(async () => {
56
+ onPressGotoSmartAccountDetail.props.onPress();
57
+ });
58
+
59
+ expect(mockedGotoSmartAccountDetail).toHaveBeenCalled();
60
+
61
+ const onPressMore = instance.find(
62
+ (el) =>
63
+ el.props.testID === TESTID.SMART_ACCOUNT_ITEM_PRESSMORE &&
64
+ el.type === TouchableOpacity
65
+ );
66
+
67
+ await act(async () => {
68
+ onPressMore.props.onPress();
69
+ });
70
+ expect(mockedOnShowMenuMore).toHaveBeenCalled();
71
+ });
72
+ });
@@ -189,7 +189,7 @@ const ThreePhasePowerConsumption = memo(({ summaryDetail }) => {
189
189
  }, [listConfigs]);
190
190
 
191
191
  const [startDate, setStartDate] = useState(
192
- moment().subtract(7, 'days').valueOf()
192
+ moment().subtract(6, 'days').valueOf()
193
193
  );
194
194
  const [endDate, setEndDate] = useState(moment().valueOf());
195
195
  const [groupBy, setGroupBy] = useState('date');
@@ -251,6 +251,7 @@ const ThreePhasePowerConsumption = memo(({ summaryDetail }) => {
251
251
  startDate={startDate}
252
252
  setEndDate={setEndDate}
253
253
  setStartDate={setStartDate}
254
+ groupBy={groupBy}
254
255
  setGroupBy={setGroupBy}
255
256
  configuration={{
256
257
  type: 'horizontal_bar_chart',
@@ -78,7 +78,7 @@ const PowerConsumption = memo(({ summaryDetail }) => {
78
78
  dataTotal.push(totalPower);
79
79
 
80
80
  const [startDate, setStartDate] = useState(
81
- moment().subtract(7, 'days').valueOf()
81
+ moment().subtract(6, 'days').valueOf()
82
82
  );
83
83
  const [endDate, setEndDate] = useState(moment().valueOf());
84
84
  const [groupBy, setGroupBy] = useState('date');
@@ -10,13 +10,13 @@ const ItemTemperature = memo((props) => {
10
10
  const { svgMain, title, des, value } = props;
11
11
  return (
12
12
  <View style={styles.container}>
13
- <Text size={14} color={Colors.Gray8} style={styles.textTitle}>
13
+ <Text type="H4" color={Colors.Gray8} style={styles.textTitle}>
14
14
  {title}
15
15
  </Text>
16
16
  <Text size={24} color={Colors.Gray9} style={styles.textValue}>
17
17
  {value}
18
18
  </Text>
19
- <Text size={12} color={Colors.Gray8}>
19
+ <Text type="H4" color={Colors.Gray8}>
20
20
  {des}
21
21
  </Text>
22
22
  <View style={styles.boxSvg}>{svgMain}</View>
@@ -450,9 +450,9 @@ const styles = StyleSheet.create({
450
450
  position: 'absolute',
451
451
  fontSize: 20,
452
452
  transform: [{ rotate: '90deg' }],
453
- left: -28,
453
+ left: -40,
454
454
  bottom: 8,
455
- width: 115,
455
+ width: 150,
456
456
  height: 28,
457
457
  textAlign: 'center',
458
458
  },
@@ -471,7 +471,7 @@
471
471
  "scan_qr_code_at_%{spot}": "Scan QR code at spot %{spot}",
472
472
  "extend": "Extend",
473
473
  "verify_qr_code": "Verifying code...",
474
- "tds_infomation": "TDS Infomation",
474
+ "tds_information": "TDS Information",
475
475
  "what_is_tds_title": "What is TDS?",
476
476
  "what_is_tds_text": "TDS: is the total dissolved solids present in a certain amount of water - (the total number of charged ions, including salts, minerals or metals) that exist in a certain volume of water. High TDS is also responsible for turbidity and sedimentation in water. TDS is expressed in mg / L or parts per million (parts per million).",
477
477
  "tds_guidlines_title": "TDS guidlines for drinking water",
@@ -744,7 +744,7 @@
744
744
  "rssi_node": "RSSI Node",
745
745
  "edit_actions_list": "Edit Actions List",
746
746
  "des_edit_actions_list": "Hold and hover to rearrange actions order",
747
- "please_add_your_phone_number_and_chip_name": "Please add gateway name",
747
+ "please_add_your_phone_number_and_chip_name": "Please add your phone number and chip name",
748
748
  "phone_number_of_data_sim": "Phone number of data sim",
749
749
  "select_a_sub_unit": "Select a sub-unit that you want to add this gateway",
750
750
  "all_camera": "All Cameras",
@@ -781,6 +781,8 @@
781
781
  "Script": "Script",
782
782
  "One-Tap": "One-Tap",
783
783
  "Scenario": "Scenario",
784
+ "one_tap": "One-Tap",
785
+ "scenario": "Scenario",
784
786
  "automation": "Automation",
785
787
  "set_schedule": "Set schedule",
786
788
  "select_date": "Select date",
@@ -854,5 +856,15 @@
854
856
  "volume": "Volume",
855
857
  "this_notification_will_be_updated_soon": "This notification will be updated soon",
856
858
  "text_submit": "Submit",
857
- "tap_to_add_new_schedule": "Tap + to add new schedule"
859
+ "remove_account": "Remove Account",
860
+ "text_sub_unit_not_have_device": "You don't have sub-unit with a device to control",
861
+ "tap_to_add_new_schedule": "Tap + to add new schedule",
862
+ "confirmation": "Confirmation",
863
+ "enter_yes_to_perform": "You need to enter 'YES' to perform this action",
864
+ "please_enter_correct":"Please enter correct",
865
+ "add_schedule": "Add Schedule",
866
+ "curtain_opens": "Curtain opens",
867
+ "curtain_closes": "Curtain closes",
868
+ "schedule_name": "Schedule name",
869
+ "enter_name": "Enter name"
858
870
  }
@@ -57,7 +57,7 @@
57
57
  "%{number}_filter_need_to_be_replaced": "%{number} lõi lọc cần được thay thế.",
58
58
  "replace_all_filters": "Tất cả lõi lọc cần được thay thế.",
59
59
  "auth_already_have_an_account": "Bạn đã có tài khoản? ",
60
- "tds_infomation": "Chỉ số TDS",
60
+ "tds_information": "Chỉ số TDS",
61
61
  "what_is_tds_title": "TDS là gì?",
62
62
  "set_a_number": "Đặt một số",
63
63
  "add_action": "Thêm hành động",
@@ -747,7 +747,7 @@
747
747
  "request_fail": "Request fail",
748
748
  "modbus_fail": "Modbus failrate",
749
749
  "rssi_node": "RSSI Node",
750
- "please_add_your_phone_number_and_chip_name": "Vui lòng thêm tên gateway",
750
+ "please_add_your_phone_number_and_chip_name": "Vui lòng thêm số điện thoại và tên chip của bạn",
751
751
  "phone_number_of_data_sim": "Số điện thoại của dữ liệu sim",
752
752
  "select_a_sub_unit": "Lựa chọn một khu vực mà bạn muốn thêm gateway",
753
753
  "all_camera": "All Cameras",
@@ -783,6 +783,8 @@
783
783
  "Script": "Kịch bản",
784
784
  "One-Tap": "Một chạm",
785
785
  "Scenario": "Kịch bản",
786
+ "one_tap": "Một chạm",
787
+ "scenario": "Kịch bản",
786
788
  "automation": "Tự động",
787
789
  "set_schedule": "Đặt lịch",
788
790
  "select_date": "Chọn ngày",
@@ -856,5 +858,15 @@
856
858
  "this_notification_will_be_updated_soon": "Thông báo này sẽ sớm được cập nhật",
857
859
  "text_submit": "Xác nhận",
858
860
  "tap_to_add_new_schedule": "Nhấn + để thêm lịch trình mới",
859
- "choose_at_least_one": "Vui lòng chọn ít nhất 1 thiết bị."
861
+ "remove_account": "Xóa tài khoản",
862
+ "text_subunit_not_have_device": "Bạn không có khu vực nào có thiết bị để điều khiển",
863
+ "choose_at_least_one": "Vui lòng chọn ít nhất 1 thiết bị.",
864
+ "confirmation": "Xác Nhận",
865
+ "enter_yes_to_perform": "Bạn cần nhập 'YES' để thực hiện hành động này",
866
+ "please_enter_correct":"Vui lòng nhập đúng",
867
+ "add_schedule": "Thêm lịch trình",
868
+ "curtain_opens": "Rèm mở",
869
+ "curtain_closes": "Rèm đóng",
870
+ "schedule_name": "Tên lịch trình",
871
+ "enter_name": "Nhập tên"
860
872
  }
@@ -121,6 +121,7 @@ const Routes = {
121
121
  ListDeviceSmartAccount: 'ListDeviceSmartAccount',
122
122
  SmartAccountConnecting: 'SmartAccountConnecting',
123
123
  EmergencySetting: 'EmergencySetting',
124
+ ConfirmUnitDeletion: 'ConfirmUnitDeletion',
124
125
  };
125
126
 
126
127
  export default Routes;
@@ -120,6 +120,10 @@ export const removeFromString = (str, index) => {
120
120
  return str.substr(0, index) + str.substr(index + 1);
121
121
  };
122
122
 
123
+ export const arePropsEqual = (prevProps, nextProps) => {
124
+ return JSON.stringify(prevProps) === JSON.stringify(nextProps);
125
+ };
126
+
123
127
  export default {
124
128
  validateEmail,
125
129
  isObjectEmpty,