@eohjsc/react-native-smart-city 0.2.79 → 0.2.83

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 (60) hide show
  1. package/package.json +4 -4
  2. package/src/commons/ActionGroup/CurtainButtonTemplate.js +32 -21
  3. package/src/commons/ActionGroup/NumberUpDownActionTemplate.js +8 -6
  4. package/src/commons/ActionGroup/OnOffTemplate/index.js +11 -3
  5. package/src/commons/ActionGroup/OneBigButtonTemplate.js +10 -7
  6. package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +5 -2
  7. package/src/commons/ActionGroup/StatesGridActionTemplate.js +7 -3
  8. package/src/commons/ActionGroup/ThreeButtonTemplate.js +33 -24
  9. package/src/commons/ActionGroup/__test__/OnOffTemplate.test.js +18 -6
  10. package/src/commons/ActionGroup/__test__/OneBigButtonTemplate.test.js +9 -1
  11. package/src/commons/ActionGroup/__test__/OptionsDropdownTemplate.test.js +25 -13
  12. package/src/commons/ActionGroup/__test__/index.test.js +48 -14
  13. package/src/commons/Device/HorizontalBarChart.js +8 -2
  14. package/src/commons/Device/ItemDevice.js +8 -5
  15. package/src/commons/Device/LinearChart.js +1 -1
  16. package/src/commons/EmergencyButton/AlertSendConfirm.js +2 -2
  17. package/src/commons/EmergencyButton/AlertSent.js +2 -2
  18. package/src/commons/ImagePicker/__test__/ImagePicker.test.js +24 -3
  19. package/src/commons/MediaPlayerDetail/index.js +1 -0
  20. package/src/commons/SubUnit/Favorites/index.js +2 -0
  21. package/src/commons/SubUnit/ShortDetail.js +7 -1
  22. package/src/configs/API.js +2 -4
  23. package/src/configs/Constants.js +5 -0
  24. package/src/iot/RemoteControl/Internet.js +8 -1
  25. package/src/iot/RemoteControl/index.js +4 -2
  26. package/src/screens/AddCommon/SelectSubUnit.js +6 -0
  27. package/src/screens/AddNewGateway/__test__/SetupGateway.test.js +52 -0
  28. package/src/screens/AllCamera/index.js +76 -44
  29. package/src/screens/Device/EditDevice/__test__/EditDevice.test.js +2 -2
  30. package/src/screens/Device/EditDevice/index.js +2 -2
  31. package/src/screens/Device/__test__/detail.test.js +18 -11
  32. package/src/screens/Device/components/SensorConnectStatusViewHeader.js +2 -2
  33. package/src/screens/Device/components/SensorDisplayItem.js +2 -2
  34. package/src/screens/Device/detail.js +58 -20
  35. package/src/screens/Notification/__test__/NotificationItem.test.js +186 -14
  36. package/src/screens/Notification/components/NotificationItem.js +147 -3
  37. package/src/screens/Notification/index.js +4 -1
  38. package/src/screens/Notification/styles/NotificationItemStyles.js +3 -3
  39. package/src/screens/Sharing/SelectUser.js +17 -10
  40. package/src/screens/Sharing/__test__/SelectUser.test.js +73 -0
  41. package/src/screens/SubUnit/AddSubUnit.js +4 -1
  42. package/src/screens/SubUnit/__test__/AddSubUnit.test.js +148 -0
  43. package/src/screens/Unit/Detail.js +28 -4
  44. package/src/screens/Unit/ManageUnit.js +88 -32
  45. package/src/screens/Unit/ManageUnitStyles.js +20 -0
  46. package/src/screens/Unit/SmartAccount.js +6 -2
  47. package/src/screens/Unit/Summaries.js +2 -2
  48. package/src/screens/Unit/__test__/CheckSendEmail.test.js +10 -0
  49. package/src/screens/Unit/__test__/Detail.test.js +53 -0
  50. package/src/screens/Unit/__test__/ManageUnit.test.js +69 -0
  51. package/src/screens/Unit/__test__/SmartAccount.test.js +37 -8
  52. package/src/screens/Unit/components/__test__/SharedUnit.test.js +21 -2
  53. package/src/screens/Unit/hook/useStateAlertRemove.js +1 -1
  54. package/src/screens/UnitSummary/__test__/index.test.js +3 -3
  55. package/src/screens/UnitSummary/components/RunningDevices/__test__/index.test.js +2 -2
  56. package/src/screens/UnitSummary/index.js +52 -9
  57. package/src/utils/Apis/axios.js +7 -2
  58. package/src/utils/I18n/translations/en.json +10 -1
  59. package/src/utils/I18n/translations/vi.json +10 -1
  60. package/src/{screens/Notification → utils}/Monitor.js +1 -1
@@ -199,4 +199,152 @@ describe('Test AddSubUnit', () => {
199
199
  visibilityTime: 1000,
200
200
  });
201
201
  });
202
+
203
+ test('test create Unit', async () => {
204
+ const response = {
205
+ success: true,
206
+ status: 200,
207
+ data: {
208
+ id: 2,
209
+ },
210
+ };
211
+ route.params = {
212
+ ...route.params,
213
+ location: 'Unit address',
214
+ isAddUnit: true,
215
+ };
216
+
217
+ axios.post.mockImplementation(async () => {
218
+ return response;
219
+ });
220
+
221
+ await act(async () => {
222
+ tree = await create(wrapComponent(route));
223
+ });
224
+
225
+ const instance = tree.root;
226
+ const viewButtonBottom = await makeValidateData(instance);
227
+
228
+ await act(async () => {
229
+ await viewButtonBottom.props.onRightClick();
230
+ });
231
+ expect(axios.post).toHaveBeenCalled();
232
+ expect(mockedNavigate).toHaveBeenCalledWith(Routes.UnitStack, {
233
+ screen: Routes.UnitDetail,
234
+ params: {
235
+ unitId: response.data.id,
236
+ routeName: Routes.DashboardStack,
237
+ },
238
+ });
239
+ });
240
+
241
+ test('test choose Location', async () => {
242
+ route.params = {
243
+ ...route.params,
244
+ location: '',
245
+ isAddUnit: true,
246
+ };
247
+ await act(async () => {
248
+ tree = await create(wrapComponent(route));
249
+ });
250
+
251
+ const instance = tree.root;
252
+ const buttonChooseLocation = instance.find(
253
+ (el) => el.props.testID === TESTID.ADD_SUB_UNIT_BUTTON_CHOOSE_LOCATION
254
+ );
255
+ act(() => {
256
+ buttonChooseLocation.props.onPress();
257
+ });
258
+ expect(mockedNavigate).toHaveBeenCalledWith(Routes.AddLocationMaps);
259
+ });
260
+
261
+ test('test create Unit Fail', async () => {
262
+ const response = {
263
+ success: false,
264
+ status: 400,
265
+ };
266
+ route.params = {
267
+ ...route.params,
268
+ location: 'Unit address',
269
+ isAddUnit: true,
270
+ };
271
+
272
+ axios.post.mockImplementation(async () => {
273
+ return response;
274
+ });
275
+
276
+ await act(async () => {
277
+ tree = await create(wrapComponent(route));
278
+ });
279
+
280
+ const instance = tree.root;
281
+ const viewButtonBottom = await makeValidateData(instance);
282
+
283
+ await act(async () => {
284
+ await viewButtonBottom.props.onRightClick();
285
+ });
286
+ expect(axios.post).toHaveBeenCalled();
287
+ expect(Toast.show).toHaveBeenCalledWith({
288
+ type: 'error',
289
+ position: 'bottom',
290
+ text1: getTranslate('en', 'text_create_unit_fail'),
291
+ visibilityTime: 1000,
292
+ });
293
+ });
294
+
295
+ test('test create sub-unit type AddHassiDevice', async () => {
296
+ const response = {
297
+ status: 200,
298
+ success: true,
299
+ data: {},
300
+ };
301
+ route.params = {
302
+ ...route.params,
303
+ addType: 'AddHassiDevice',
304
+ };
305
+
306
+ axios.post.mockImplementation(async () => {
307
+ return response;
308
+ });
309
+
310
+ await act(async () => {
311
+ tree = await create(wrapComponent(route));
312
+ });
313
+ const instance = tree.root;
314
+ const viewButtonBottom = await makeValidateData(instance);
315
+ await act(async () => {
316
+ await viewButtonBottom.props.onRightClick();
317
+ });
318
+ expect(axios.post).toHaveBeenCalled();
319
+ expect(mockedGoBack).toHaveBeenCalled();
320
+ });
321
+
322
+ test('test create sub-unit type AddNewGateway', async () => {
323
+ const response = {
324
+ status: 200,
325
+ success: true,
326
+ data: {},
327
+ };
328
+ route.params = {
329
+ ...route.params,
330
+ addType: 'AddNewGateway',
331
+ };
332
+
333
+ axios.post.mockImplementation(async () => {
334
+ return response;
335
+ });
336
+
337
+ await act(async () => {
338
+ tree = await create(wrapComponent(route));
339
+ });
340
+ const instance = tree.root;
341
+ const viewButtonBottom = await makeValidateData(instance);
342
+ await act(async () => {
343
+ await viewButtonBottom.props.onRightClick();
344
+ });
345
+ expect(axios.post).toHaveBeenCalled();
346
+ expect(mockedNavigate).toHaveBeenCalledWith(Routes.AddCommonSelectSubUnit, {
347
+ ...route.params,
348
+ });
349
+ });
202
350
  });
@@ -2,6 +2,7 @@ import React, { useCallback, useContext, useEffect, useState } from 'react';
2
2
  import { AppState, RefreshControl, View } from 'react-native';
3
3
  import { useIsFocused } from '@react-navigation/native';
4
4
  import { useTranslations } from '../../hooks/Common/useTranslations';
5
+ import { useNetInfo } from '@react-native-community/netinfo';
5
6
 
6
7
  import styles from './styles';
7
8
  import AddMenu from './AddMenu';
@@ -26,7 +27,11 @@ import { useNavigation } from '@react-navigation/native';
26
27
  import Routes from '../../utils/Route';
27
28
  import SubUnitAutomate from '../../commons/SubUnit/OneTap';
28
29
  import SubUnitFavorites from '../../commons/SubUnit/Favorites';
29
- import { AUTOMATE_TYPE } from '../../configs/Constants';
30
+ import { AUTOMATE_TYPE, NOTIFICATION_TYPES } from '../../configs/Constants';
31
+ import {
32
+ watchNotificationData,
33
+ unwatchNotificationData,
34
+ } from '../../utils/Monitor';
30
35
 
31
36
  const UnitDetail = ({ route }) => {
32
37
  const t = useTranslations();
@@ -43,6 +48,7 @@ const UnitDetail = ({ route }) => {
43
48
  const isFocused = useIsFocused();
44
49
  const { stateData, setAction } = useContext(SCContext);
45
50
  const { navigate } = useNavigation();
51
+ const user = useSCContextSelector((state) => state?.auth?.account?.user);
46
52
  const isLavidaSource = useSCContextSelector(
47
53
  (state) => state.app.isLavidaSource
48
54
  );
@@ -57,6 +63,7 @@ const UnitDetail = ({ route }) => {
57
63
  automates: [],
58
64
  });
59
65
  const [isGGHomeConnected, setIsGGHomeConnected] = useState(false);
66
+ const [isNetworkConnected, setIsNetworkConnected] = useState(true);
60
67
  const [station, setStation] = useState({});
61
68
  const [indexStation, setIndexStation] = useState(0);
62
69
  const [showAdd, setShowAdd, setHideAdd] = useBoolean();
@@ -68,6 +75,8 @@ const UnitDetail = ({ route }) => {
68
75
 
69
76
  const { isOwner } = useIsOwnerOfUnit(unit.user_id);
70
77
 
78
+ const netInfo = useNetInfo();
79
+
71
80
  const handleFullScreen = (data) => {
72
81
  setIsFullScreen(!isFullScreen);
73
82
  setDataFullScreen(data);
@@ -144,9 +153,13 @@ const UnitDetail = ({ route }) => {
144
153
  );
145
154
  }, [t, unitId]);
146
155
 
147
- const onRefresh = useCallback(() => {
148
- fetchDetails();
149
- }, [fetchDetails]);
156
+ const onRefresh = useCallback(
157
+ (data = { content_code: NOTIFICATION_TYPES.NOTIFY_RENAME_UNIT }) => {
158
+ data.content_code === NOTIFICATION_TYPES.NOTIFY_RENAME_UNIT &&
159
+ fetchDetails();
160
+ },
161
+ [fetchDetails]
162
+ );
150
163
 
151
164
  const handleAppStateChange = useCallback(
152
165
  (nextAppState) => {
@@ -166,6 +179,10 @@ const UnitDetail = ({ route }) => {
166
179
  };
167
180
  }, [handleAppStateChange]);
168
181
 
182
+ useEffect(() => {
183
+ setIsNetworkConnected(netInfo.isConnected);
184
+ }, [netInfo.isConnected]);
185
+
169
186
  const handleGoogleHomeConnect = useCallback(
170
187
  async (options) => {
171
188
  let isConnected = await googleHomeConnect(options); // this may wrong if have multiple connection
@@ -272,6 +289,7 @@ const UnitDetail = ({ route }) => {
272
289
  isOwner={isOwner}
273
290
  favorites={favorites}
274
291
  wrapItemStyle={styles.wrapItemStyle}
292
+ isNetworkConnected={isNetworkConnected}
275
293
  isGGHomeConnected={isGGHomeConnected}
276
294
  />
277
295
  );
@@ -298,6 +316,7 @@ const UnitDetail = ({ route }) => {
298
316
  <ShortDetailSubUnit
299
317
  unit={unit}
300
318
  station={station}
319
+ isNetworkConnected={isNetworkConnected}
301
320
  isGGHomeConnected={isGGHomeConnected}
302
321
  />
303
322
  );
@@ -312,6 +331,11 @@ const UnitDetail = ({ route }) => {
312
331
  navigate(isLavidaSource ? Routes.SmartHomeDashboard : Routes.Dashboard);
313
332
  }, [isLavidaSource, navigate]);
314
333
 
334
+ useEffect(() => {
335
+ watchNotificationData(user, onRefresh);
336
+ return () => unwatchNotificationData(user);
337
+ }, [user, onRefresh]);
338
+
315
339
  return (
316
340
  <WrapParallaxScrollView
317
341
  uriImg={unit.background}
@@ -1,4 +1,4 @@
1
- import React, { useState, useCallback, useEffect } from 'react';
1
+ import React, { useState, useCallback, useEffect, useMemo } from 'react';
2
2
  import { View, TouchableOpacity, Image, Platform } from 'react-native';
3
3
  import Animated from 'react-native-reanimated';
4
4
  import { useTranslations } from '../../hooks/Common/useTranslations';
@@ -21,6 +21,7 @@ import { IconOutline } from '@ant-design/icons-react-native';
21
21
  import styles from './ManageUnitStyles';
22
22
  import { useNavigation } from '@react-navigation/native';
23
23
  import { ModalCustom } from '../../commons/Modal';
24
+ import { Icon } from '@ant-design/react-native';
24
25
 
25
26
  const ButtonWrapper = ({
26
27
  onPress,
@@ -29,17 +30,26 @@ const ButtonWrapper = ({
29
30
  value,
30
31
  valueColor,
31
32
  children,
33
+ icon,
32
34
  }) => {
33
35
  return (
34
36
  <TouchableOpacity
35
37
  onPress={onPress}
36
38
  testID={testId}
37
- style={styles.buttonWrapper}
39
+ style={!icon ? styles.buttonWrapper : styles.buttonWrapperAvatar}
38
40
  >
39
41
  <View style={styles.buttonInfo}>
40
- <Text type="H4" semibold>
41
- {title}
42
- </Text>
42
+ {!icon ? (
43
+ <Text type="H4" semibold>
44
+ {title}
45
+ </Text>
46
+ ) : (
47
+ (icon && <Image source={{ uri: icon }} style={styles.avatar} />) || (
48
+ <View style={styles.avatar}>
49
+ <Icon name={'user'} size={27} />
50
+ </View>
51
+ )
52
+ )}
43
53
  <View style={styles.buttonValue}>
44
54
  <Text
45
55
  type="Body"
@@ -66,15 +76,29 @@ const ManageUnit = ({ route }) => {
66
76
  name: unit.name,
67
77
  address: unit.address,
68
78
  background: unit.background,
79
+ icon: unit.icon,
69
80
  });
70
81
 
71
82
  const [unitName, setUnitName] = useState(unit.name);
72
- const [imageUrl, setImageUrl] = useState('');
83
+ const [imageUrlBackground, setImageUrlBackground] = useState('');
84
+ const [imageUrlIcon, setImageUrlIcon] = useState('');
85
+ const [checkSelectImage, setCheckSelectImage] = useState('');
73
86
  const [showImagePicker, setShowImagePicker] = useState(false);
74
87
 
88
+ const onUpdateImage = useCallback(
89
+ async (data) => {
90
+ if (checkSelectImage === 'background') {
91
+ setImageUrlBackground(data);
92
+ } else {
93
+ setImageUrlIcon(data);
94
+ }
95
+ },
96
+ [checkSelectImage]
97
+ );
98
+
75
99
  const updateUnit = useCallback(
76
- async (bodyData, headers) => {
77
- const formData = createFormData(bodyData, ['background']);
100
+ async (bodyData, headers, dataInput) => {
101
+ const formData = createFormData(bodyData, [dataInput]);
78
102
 
79
103
  const { success, data } = await axiosPatch(
80
104
  API.UNIT.MANAGE_UNIT(unit.id),
@@ -110,20 +134,37 @@ const ManageUnit = ({ route }) => {
110
134
  setHideEdit(true);
111
135
  }, [unitName, setHideEdit, updateUnit]);
112
136
 
113
- const handleChoosePhoto = useCallback(() => {
114
- setShowImagePicker(true);
115
- }, [setShowImagePicker]);
137
+ const handleChoosePhoto = useCallback(
138
+ (text) => {
139
+ setCheckSelectImage(text);
140
+ setShowImagePicker(true);
141
+ },
142
+ [setShowImagePicker]
143
+ );
116
144
 
117
145
  useEffect(() => {
118
- if (imageUrl) {
146
+ if (imageUrlBackground) {
119
147
  updateUnit(
120
- { background: imageUrl },
148
+ { background: imageUrlBackground },
121
149
  {
122
150
  headers: { 'Content-Type': 'multipart/form-data' },
123
- }
151
+ },
152
+ 'background'
124
153
  );
125
154
  }
126
- }, [imageUrl, updateUnit]);
155
+ }, [imageUrlBackground, updateUnit]);
156
+
157
+ useEffect(() => {
158
+ if (imageUrlIcon) {
159
+ updateUnit(
160
+ { icon: imageUrlIcon },
161
+ {
162
+ headers: { 'Content-Type': 'multipart/form-data' },
163
+ },
164
+ 'icon'
165
+ );
166
+ }
167
+ }, [imageUrlIcon, updateUnit]);
127
168
 
128
169
  const [showRemove, setshowRemove, setHideRemove] = useBoolean();
129
170
  const goRemove = useCallback(async () => {
@@ -160,6 +201,12 @@ const ManageUnit = ({ route }) => {
160
201
  compressImageQuality: 0.8,
161
202
  };
162
203
 
204
+ const SensorNumbers = useMemo(() => {
205
+ const stations = unit?.stations || [];
206
+ return stations.filter((item) => !item.isFavorites && !item.isOneTap)
207
+ .length;
208
+ }, [unit.stations]);
209
+
163
210
  return (
164
211
  <>
165
212
  <WrapHeaderScrollable
@@ -169,6 +216,12 @@ const ManageUnit = ({ route }) => {
169
216
  <View style={styles.wraper}>
170
217
  {isOwner && (
171
218
  <>
219
+ <ButtonWrapper
220
+ onPress={() => handleChoosePhoto('avatar')}
221
+ value={t('icon_unit')}
222
+ icon={unitData.icon}
223
+ testID={TESTID.MANAGE_UNIT_CHANGE_PHOTO}
224
+ />
172
225
  <ButtonWrapper
173
226
  onPress={setshowEdit}
174
227
  testID={TESTID.MANAGE_UNIT_CHANGE_NAME}
@@ -187,10 +240,11 @@ const ManageUnit = ({ route }) => {
187
240
  <ButtonWrapper
188
241
  onPress={goToManageSubUnit}
189
242
  title={t('manage_sub_units')}
190
- value={`${unit?.stations?.length} sub-units`}
243
+ value={`${SensorNumbers} sub-units`}
244
+ testID={TESTID.MANAGE_UNIT_GO_TO_SUBUNIT}
191
245
  />
192
246
  <ButtonWrapper
193
- onPress={handleChoosePhoto}
247
+ onPress={() => handleChoosePhoto('background')}
194
248
  title={t('unit_wallpaper')}
195
249
  value={t('tap_to_change')}
196
250
  valueColor={Colors.Orange}
@@ -213,7 +267,7 @@ const ManageUnit = ({ route }) => {
213
267
  <ImagePicker
214
268
  showImagePicker={showImagePicker}
215
269
  setShowImagePicker={setShowImagePicker}
216
- setImageUrl={setImageUrl}
270
+ setImageUrl={onUpdateImage}
217
271
  optionsCapture={options}
218
272
  testID={TESTID.MANAGE_UNIT_IMAGE_PICKER}
219
273
  />
@@ -221,20 +275,22 @@ const ManageUnit = ({ route }) => {
221
275
  )}
222
276
  </View>
223
277
  </WrapHeaderScrollable>
224
- <TouchableOpacity
225
- style={styles.removeButton}
226
- onPress={setshowRemove}
227
- testID={TESTID.MANAGE_UNIT_SHOW_REMOVE}
228
- >
229
- <Text
230
- type={'H4'}
231
- semibold
232
- color={Colors.Red}
233
- style={styles.removeBorderBottom}
278
+ {!showEdit && (
279
+ <TouchableOpacity
280
+ style={styles.removeButton}
281
+ onPress={setshowRemove}
282
+ testID={TESTID.MANAGE_UNIT_SHOW_REMOVE}
234
283
  >
235
- {t('remove_unit')}
236
- </Text>
237
- </TouchableOpacity>
284
+ <Text
285
+ type={'H4'}
286
+ semibold
287
+ color={Colors.Red}
288
+ style={styles.removeBorderBottom}
289
+ >
290
+ {t('remove_unit')}
291
+ </Text>
292
+ </TouchableOpacity>
293
+ )}
238
294
  <ModalCustom
239
295
  isVisible={showEdit}
240
296
  onBackButtonPress={setHideEdit}
@@ -250,7 +306,7 @@ const ManageUnit = ({ route }) => {
250
306
  </Text>
251
307
  </View>
252
308
  <_TextInput
253
- defaultValue={unitName}
309
+ defaultValue={unitData?.name}
254
310
  onChange={(value) => setUnitName(value)}
255
311
  textInputStyle={styles.textInputStyle}
256
312
  wrapStyle={styles.textInputWrapStyle}
@@ -16,6 +16,12 @@ export default StyleSheet.create({
16
16
  borderBottomWidth: 0.5,
17
17
  borderBottomColor: Colors.Gray6,
18
18
  },
19
+ buttonWrapperAvatar: {
20
+ paddingTop: 14,
21
+ paddingBottom: 24,
22
+ borderBottomWidth: 0.5,
23
+ borderBottomColor: Colors.Gray7,
24
+ },
19
25
  buttonInfo: {
20
26
  flex: 1,
21
27
  flexDirection: 'row',
@@ -29,6 +35,7 @@ export default StyleSheet.create({
29
35
  },
30
36
  value: {
31
37
  marginRight: 20,
38
+ fontSize: 14,
32
39
  },
33
40
  location: {
34
41
  marginTop: 16,
@@ -104,4 +111,17 @@ export default StyleSheet.create({
104
111
  textInputWrapStyle: {
105
112
  marginTop: 0,
106
113
  },
114
+ avatar: {
115
+ height: 50,
116
+ width: 50,
117
+ borderRadius: 25,
118
+ borderWidth: 0.5,
119
+ borderColor: Colors.Gray5,
120
+ justifyContent: 'center',
121
+ alignItems: 'center',
122
+ },
123
+ wrapAvatar: {
124
+ flexDirection: 'row',
125
+ justifyContent: 'space-between',
126
+ },
107
127
  });
@@ -18,6 +18,7 @@ import { usePopover, useBoolean } from '../../hooks/Common';
18
18
  import { MenuActionMore, AlertAction } from '../../commons';
19
19
  import { useTranslations } from '../../hooks/Common/useTranslations';
20
20
  import { useStateAlertRemove } from '../Unit/hook/useStateAlertRemove';
21
+ import { ToastBottomHelper } from '../../utils/Utils';
21
22
 
22
23
  const ListSmartAccount = ({ route }) => {
23
24
  const { unitId } = route?.params || {};
@@ -76,8 +77,11 @@ const ListSmartAccount = ({ route }) => {
76
77
  const { success } = await axiosDelete(
77
78
  API.SMART_ACCOUNT.REMOVE_SMART_ACCOUNT(id)
78
79
  );
79
- success && getAllSmartAccounts();
80
- }, [getAllSmartAccounts, hideAlertAction]);
80
+ if (success) {
81
+ ToastBottomHelper.success(t('removed_successfully'));
82
+ getAllSmartAccounts();
83
+ }
84
+ }, [getAllSmartAccounts, hideAlertAction, t]);
81
85
 
82
86
  const listMenuItem = useMemo(() => {
83
87
  return [{ action: 'remove', text: t('remove_account') }];
@@ -34,8 +34,8 @@ const Summaries = memo(({ unit }) => {
34
34
  const goToSummary = useCallback(
35
35
  (summary) => {
36
36
  navigation.navigate(Routes.UnitSummary, {
37
- summary,
38
- unit,
37
+ summaryData: summary,
38
+ unitData: unit,
39
39
  });
40
40
  },
41
41
  [navigation, unit]
@@ -26,6 +26,16 @@ jest.mock('../../../iot/RemoteControl/GoogleHome', () => ({
26
26
 
27
27
  jest.mock('axios');
28
28
 
29
+ jest.mock('@react-native-community/netinfo', () => {
30
+ return {
31
+ useNetInfo: () => {
32
+ return {
33
+ isConnected: true,
34
+ };
35
+ },
36
+ };
37
+ });
38
+
29
39
  const wrapComponent = (route, unitData, account) => (
30
40
  <SCProvider initState={mockSCStore({})}>
31
41
  <UnitDetail
@@ -62,6 +62,16 @@ jest.mock('home-assistant-js-websocket', () => {
62
62
  };
63
63
  });
64
64
 
65
+ jest.mock('@react-native-community/netinfo', () => {
66
+ return {
67
+ useNetInfo: () => {
68
+ return {
69
+ isConnected: true,
70
+ };
71
+ },
72
+ };
73
+ });
74
+
65
75
  jest.mock('axios');
66
76
 
67
77
  describe('Test UnitDetail', () => {
@@ -452,7 +462,50 @@ describe('Test UnitDetail', () => {
452
462
  expect(fullView).toHaveLength(1);
453
463
  expect(fullView[0].props.isVisible).toEqual(false);
454
464
  });
465
+ test('onPress subunit camera devices', async () => {
466
+ const unitData = {
467
+ stations: [
468
+ {
469
+ camera_devices: [
470
+ {
471
+ configuration: {
472
+ id: 4,
473
+ name: 'Camera cửa nhà xe',
474
+ uri: 'rtsp://admin:hd543211@/ISAPI/Streaming/Channels/101/',
475
+ preview_uri:
476
+ 'rtsp://admin:hd543211@/ISAPI/Streaming/Channels/101/',
477
+ playback: 'rtsp://admin:hd543211@/Streaming/tracks/101/',
478
+ },
479
+ id: 41,
480
+ order: 1,
481
+ template: 'camera',
482
+ type: 'camera',
483
+ },
484
+ ],
485
+ },
486
+ ],
487
+ };
488
+
489
+ await act(async () => {
490
+ tree = await renderer.create(
491
+ wrapComponent({ params: { ...route.params, unitData } }, account)
492
+ );
493
+ });
494
+ const instance = tree.root;
495
+ const CameraDeviceViews = instance.findAllByType(CameraDevice);
496
+ expect(CameraDeviceViews).toHaveLength(1);
497
+ const goDetailButton = tree.root.findAll(
498
+ (el) =>
499
+ el.props.testID === TESTID.SUB_UNIT_GO_DETAIL &&
500
+ el.type === TouchableOpacity
501
+ );
455
502
 
503
+ expect(goDetailButton).toHaveLength(2);
504
+ await act(async () => {
505
+ await goDetailButton[0].props.onPress();
506
+ });
507
+ expect(mockedNavigate).toHaveBeenCalled();
508
+ });
456
509
  test('render subunit favorites', async () => {
457
510
  const unitData = {
458
511
  stations: [