@eohjsc/react-native-smart-city 0.2.67 → 0.2.71

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 (86) hide show
  1. package/assets/images/Popover/Dashboard/SmartAccount.svg +5 -0
  2. package/assets/images/scan-qr-gateway.svg +14 -0
  3. package/assets/images/wifi-gateway.svg +18 -0
  4. package/assets/images/wifi.svg +3 -0
  5. package/package.json +3 -1
  6. package/src/Images/Common/buttonLeftCurtain@2x.png +0 -0
  7. package/src/Images/Common/buttonLeftCurtain@3x.png +0 -0
  8. package/src/Images/Common/{buttonPause-center-curtain.png → buttonPauseCurtain.png} +0 -0
  9. package/src/Images/Common/buttonPauseCurtain@2x.png +0 -0
  10. package/src/Images/Common/buttonPauseCurtain@3x.png +0 -0
  11. package/src/Images/Common/buttonRightCurtain@2x.png +0 -0
  12. package/src/Images/Common/buttonRightCurtain@3x.png +0 -0
  13. package/src/commons/Action/ItemQuickAction.js +4 -3
  14. package/src/commons/ActionGroup/CurtainButtonTemplate.js +0 -30
  15. package/src/commons/ActionGroup/CurtainButtonTemplateStyle.js +0 -12
  16. package/src/commons/ActionGroup/__test__/CurtainButtonTemplate.test.js +1 -1
  17. package/src/commons/AlertAction/index.js +4 -3
  18. package/src/commons/CameraDevice/index.js +1 -1
  19. package/src/commons/Dashboard/MyPinnedSharedUnit/__test__/MyPinnedSharedUnit.test.js +70 -0
  20. package/src/commons/Device/HistoryChart.js +18 -18
  21. package/src/commons/Device/HorizontalBarChart.js +1 -2
  22. package/src/commons/Device/ItemDevice.js +1 -13
  23. package/src/commons/Device/LinearChart.js +13 -3
  24. package/src/commons/Device/WaterQualitySensor/QualityIndicatorsItem.js +1 -1
  25. package/src/commons/SubUnit/OneTap/OneTapStyles.js +14 -5
  26. package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +5 -68
  27. package/src/commons/SubUnit/OneTap/index.js +24 -33
  28. package/src/commons/UnitSummary/ConfigHistoryChart/__test__/ConfigHistoryChart.test.js +29 -0
  29. package/src/commons/UnitSummary/ConfigHistoryChart.js +4 -6
  30. package/src/commons/ViewButtonBottom/index.js +4 -0
  31. package/src/configs/API.js +2 -0
  32. package/src/configs/BLE.js +3 -0
  33. package/src/configs/Constants.js +2 -0
  34. package/src/configs/Images.js +1 -1
  35. package/src/context/SCContext.tsx +2 -0
  36. package/src/iot/RemoteControl/Bluetooth.js +34 -6
  37. package/src/iot/RemoteControl/__test__/Bluetooth.test.js +1 -0
  38. package/src/iot/RemoteControl/__test__/index.mock.js +1 -0
  39. package/src/iot/RemoteControl/index.js +7 -2
  40. package/src/navigations/AddDeviceStack.js +2 -0
  41. package/src/navigations/AddGatewayStack.js +11 -0
  42. package/src/navigations/AddLGDeviceStack.js +2 -0
  43. package/src/navigations/AddMemberStack.js +2 -0
  44. package/src/navigations/AddSubUnitStack.js +2 -0
  45. package/src/navigations/AddUnitStack.js +2 -0
  46. package/src/navigations/EmergencyContactsStack.js +2 -0
  47. package/src/navigations/SharedStack.js +2 -0
  48. package/src/navigations/UnitStack.js +2 -0
  49. package/src/navigations/utils.js +3 -0
  50. package/src/screens/AddCommon/SelectSubUnit.js +12 -2
  51. package/src/screens/AddNewGateway/PlugAndPlay/ConnectWifiWarning.js +190 -0
  52. package/src/screens/AddNewGateway/PlugAndPlay/FirstWarning.js +73 -0
  53. package/src/screens/AddNewGateway/PlugAndPlay/GatewayWifiList.js +154 -0
  54. package/src/screens/AddNewGateway/PlugAndPlay/__test__/FirstWarning.test.js +60 -0
  55. package/src/screens/AddNewGateway/PlugAndPlay/__test__/GatewayWifiList.test.js +35 -0
  56. package/src/screens/AddNewGateway/__test__/SetupGateway.test.js +90 -0
  57. package/src/screens/AddNewOneTap/__test__/AddNewOneTap.test.js +0 -22
  58. package/src/screens/AddNewOneTap/index.js +15 -13
  59. package/src/screens/Device/components/SensorDisplayItem.js +4 -2
  60. package/src/screens/Notification/components/NotificationItem.js +24 -1
  61. package/src/screens/Notification/styles/NotificationItemStyles.js +1 -1
  62. package/src/screens/ScanChipQR/hooks/index.js +14 -5
  63. package/src/screens/SharedUnit/index.js +7 -5
  64. package/src/screens/Sharing/Components/SensorItem.js +27 -12
  65. package/src/screens/Sharing/Components/Styles/SensorItemStyles.js +4 -0
  66. package/src/screens/Sharing/SelectPermission.js +44 -32
  67. package/src/screens/Sharing/SelectUser.js +13 -1
  68. package/src/screens/Sharing/__test__/SelectPermission.test.js +2 -1
  69. package/src/screens/SubUnit/AddSubUnit.js +4 -1
  70. package/src/screens/Unit/AddMenu.js +15 -0
  71. package/src/screens/Unit/Detail.js +15 -4
  72. package/src/screens/Unit/SmartAccount.js +36 -15
  73. package/src/screens/Unit/SmartAccountStyles.js +20 -0
  74. package/src/screens/Unit/components/__test__/MyUnitDevice.test.js +10 -6
  75. package/src/screens/Unit/hook/useStateAlertRemove.js +4 -6
  76. package/src/screens/UnitSummary/components/PowerConsumption/index.js +2 -4
  77. package/src/screens/UnitSummary/components/RunningDevices/__test__/index.test.js +10 -6
  78. package/src/utils/I18n/translations/en.json +33 -4
  79. package/src/utils/I18n/translations/vi.json +34 -4
  80. package/src/utils/Route/index.js +4 -0
  81. package/src/utils/Utils.js +0 -4
  82. package/src/commons/ActionGroup/__test__/MenuActionAddSchedule.test.js +0 -71
  83. package/src/commons/ActionGroup/hooks/AccessScheduleDetailStyles.js +0 -41
  84. package/src/commons/ActionGroup/hooks/MenuActionAddSchedule.js +0 -110
  85. package/src/commons/ActionGroup/hooks/MenuActionAddScheduleStyle.js +0 -69
  86. package/src/commons/ActionGroup/hooks/RecurringDetail.js +0 -97
@@ -20,6 +20,7 @@ import FooterInfo from '../../../commons/Device/FooterInfo';
20
20
  import MediaPlayerDetail from '../../../commons/MediaPlayerDetail';
21
21
  import { standardizeCameraScreenSize } from '../../../utils/Utils';
22
22
  import { Device } from '../../../configs';
23
+ import { useSCContextSelector } from '../../../context';
23
24
 
24
25
  const { standardizeWidth, standardizeHeight } = standardizeCameraScreenSize(
25
26
  Device.screenWidth - 32
@@ -37,11 +38,12 @@ export const SensorDisplayItem = ({
37
38
  background,
38
39
  }) => {
39
40
  const t = useTranslations();
41
+ const userId = useSCContextSelector((state) => state.auth.account.user.id);
40
42
  const doAction = useCallback(
41
43
  (action, data) => {
42
- sendRemoteCommand(sensor, action, data);
44
+ sendRemoteCommand(sensor, action, data, userId);
43
45
  },
44
- [sensor]
46
+ [sensor, userId]
45
47
  );
46
48
  if (item.configuration.type === 'compass') {
47
49
  setShowWindDirection(true);
@@ -67,7 +67,7 @@ const NotificationItem = memo(({ item }) => {
67
67
  iconContent: (
68
68
  <IconComponent
69
69
  icon_outlined={'usergroup-add'}
70
- style={styles.iconInviteMember}
70
+ style={styles.backgroundSummer}
71
71
  />
72
72
  ),
73
73
  };
@@ -220,6 +220,29 @@ const NotificationItem = memo(({ item }) => {
220
220
  params: { id: booking_id },
221
221
  }),
222
222
  };
223
+ case NOTIFICATION_TYPES.NOTIFY_REMOVE_UNIT:
224
+ const unitOwerName = paramsJSON?.unit_owner_name;
225
+ const textNotify = unitOwerName
226
+ ? t('text_notification_content_remove_unit_to_member')
227
+ : t('text_notification_content_remove_unit_to_owner');
228
+ return {
229
+ content: customColorText(textNotify, arrParams),
230
+ redirect: () => null,
231
+ iconContent: (
232
+ <IconComponent icon={'home'} style={styles.backgroundSummer} />
233
+ ),
234
+ };
235
+ case NOTIFICATION_TYPES.NOTIFY_REMOVE_MEMBER:
236
+ return {
237
+ content: customColorText(
238
+ t('text_notification_content_remove_member'),
239
+ arrParams
240
+ ),
241
+ redirect: () => null,
242
+ iconContent: (
243
+ <IconComponent icon={'home'} style={styles.backgroundSummer} />
244
+ ),
245
+ };
223
246
  default:
224
247
  return {
225
248
  content: customColorText(t('this_notification_will_be_updated_soon')),
@@ -33,7 +33,7 @@ export default StyleSheet.create({
33
33
  iconNotification: {
34
34
  width: '46%',
35
35
  },
36
- iconInviteMember: {
36
+ backgroundSummer: {
37
37
  width: '46%',
38
38
  color: Colors.Summer,
39
39
  },
@@ -17,7 +17,7 @@ const useChipScan = (route) => {
17
17
  const { success, data: new_chip } = await axiosPost(
18
18
  API.UNIT.CHIP_SCAN(unit_id),
19
19
  {
20
- imei: imei,
20
+ imei: imei || body.imei,
21
21
  qr_code: body.imei,
22
22
  phone: phoneNumber,
23
23
  name: chipName,
@@ -27,10 +27,19 @@ const useChipScan = (route) => {
27
27
  }
28
28
  );
29
29
  if (success) {
30
- navigation.navigate(Routes.ConnectingGateway, {
31
- new_chip,
32
- ...route.params,
33
- });
30
+ if (new_chip.is_wifi) {
31
+ navigation.navigate(Routes.ConnectWifiWarning, {
32
+ wifi_ssid: new_chip.wifi_ssid,
33
+ wifi_pass: new_chip.wifi_pass,
34
+ chip_id: new_chip.id,
35
+ unit_id: unit_id,
36
+ });
37
+ } else {
38
+ navigation.navigate(Routes.ConnectingGateway, {
39
+ new_chip,
40
+ ...route.params,
41
+ });
42
+ }
34
43
  } else {
35
44
  navigation.goBack();
36
45
  }
@@ -1,9 +1,9 @@
1
1
  import React, { useEffect, useCallback, useState, useMemo } from 'react';
2
2
  import { View, TouchableOpacity, FlatList, RefreshControl } from 'react-native';
3
- import { useNavigation } from '@react-navigation/native';
3
+ import { useNavigation, useIsFocused } from '@react-navigation/native';
4
+
4
5
  import { axiosGet } from '../../utils/Apis/axios';
5
6
  import { useTranslations } from '../../hooks/Common/useTranslations';
6
-
7
7
  import { API } from '../../configs';
8
8
  import Text from '../../commons/Text';
9
9
  import SharedUnit from '../../commons/Unit/SharedUnit';
@@ -24,7 +24,7 @@ const Shared = () => {
24
24
  useTitleHeader(t('text_shared_with_me'));
25
25
  const navigation = useNavigation();
26
26
  const [tab, setTabActiveState] = useState(0);
27
-
27
+ const isFocused = useIsFocused();
28
28
  const [sharedUnits, setSharedUnits] = useState([]);
29
29
 
30
30
  const dataStarred = useMemo(
@@ -73,8 +73,10 @@ const Shared = () => {
73
73
  }, [language, forceUpdate]);
74
74
 
75
75
  useEffect(() => {
76
- filterSharedUnits(filter);
77
- }, [filterSharedUnits, filter]);
76
+ if (isFocused) {
77
+ filterSharedUnits(filter);
78
+ }
79
+ }, [filterSharedUnits, filter, isFocused]);
78
80
 
79
81
  const onRefresh = useCallback(() => {
80
82
  filterSharedUnits(filter);
@@ -5,11 +5,13 @@ import { Colors } from '../../../configs';
5
5
  import styles from './Styles/SensorItemStyles';
6
6
  import FImage from '../../../commons/FImage';
7
7
  import { TitleCheckBox } from '.';
8
+ import { CheckBoxCustom } from '.';
8
9
 
9
10
  const SensorItem = ({
10
11
  item = {},
11
12
  isRenderSeparated,
12
13
  onTickedChild,
14
+ onTickedSensor,
13
15
  titleGroup,
14
16
  activeItemId,
15
17
  setActiveItemId,
@@ -21,21 +23,22 @@ const SensorItem = ({
21
23
  actions = [],
22
24
  read_configs = [],
23
25
  icon_kit = '',
26
+ isChecked,
24
27
  } = item;
25
- const [expanded, setExpanded] = useState(activeItemId === id);
26
28
  const [dataConfig, setDataConfig] = useState([
27
29
  ...actions,
28
30
  ...read_configs.map((i) => ({ ...i, isConfig: true })),
29
31
  ]);
30
32
 
33
+ const expanded = activeItemId === id;
34
+
31
35
  const onPressItem = () => {
32
- setActiveItemId(id);
33
- if (activeItemId === id) {
34
- setExpanded(!expanded);
36
+ if (dataConfig.length > 0) {
37
+ expanded ? setActiveItemId(-1) : setActiveItemId(id);
38
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
35
39
  } else {
36
- setExpanded(false);
40
+ handleOnTickedSensor();
37
41
  }
38
- LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
39
42
  };
40
43
 
41
44
  const handleOnTickedChild = (idGroup, isChecked, childId) => {
@@ -56,6 +59,10 @@ const SensorItem = ({
56
59
  );
57
60
  };
58
61
 
62
+ const handleOnTickedSensor = () => {
63
+ onTickedSensor && onTickedSensor(idGroup, id, !isChecked);
64
+ };
65
+
59
66
  useEffect(() => {
60
67
  if (
61
68
  Platform.OS === 'android' &&
@@ -97,12 +104,20 @@ const SensorItem = ({
97
104
  <Text numberOfLines={1} style={styles.text} onPress={onPressItem}>
98
105
  {name}
99
106
  </Text>
100
- <IconOutline
101
- onPress={onPressItem}
102
- name={expanded ? 'up' : 'down'}
103
- size={20}
104
- color={Colors.Gray6}
105
- />
107
+ {dataConfig.length > 0 ? (
108
+ <IconOutline
109
+ onPress={onPressItem}
110
+ name={expanded ? 'up' : 'down'}
111
+ size={20}
112
+ color={Colors.Gray6}
113
+ />
114
+ ) : (
115
+ <CheckBoxCustom
116
+ isChecked={isChecked}
117
+ onPress={onPressItem}
118
+ wrapStyle={styles.checkBox}
119
+ />
120
+ )}
106
121
  </View>
107
122
  {expanded && <View style={styles.wrapExpand}>{renderData}</View>}
108
123
  {isRenderSeparated && <View style={styles.viewSeparated} />}
@@ -53,4 +53,8 @@ export default StyleSheet.create({
53
53
  wrapExpand: {
54
54
  marginTop: 10,
55
55
  },
56
+ checkBox: {
57
+ width: normalize(20),
58
+ height: normalize(20),
59
+ },
56
60
  });
@@ -38,11 +38,12 @@ const SelectPermission = ({ route }) => {
38
38
  ...i,
39
39
  isChecked,
40
40
  }));
41
- for (let sensor in data) {
42
- for (let item in data[sensor].sensors) {
43
- const itemTemp = data[sensor].sensors[item];
44
- data[sensor].sensors[item] = {
41
+ for (let station in data) {
42
+ for (let item in data[station].sensors) {
43
+ const itemTemp = data[station].sensors[item];
44
+ data[station].sensors[item] = {
45
45
  ...itemTemp,
46
+ isChecked,
46
47
  actions: itemTemp.actions.map((i) => ({
47
48
  ...i,
48
49
  isChecked,
@@ -63,6 +64,7 @@ const SelectPermission = ({ route }) => {
63
64
  isChecked,
64
65
  sensors: data[index]?.sensors.map((i) => ({
65
66
  ...i,
67
+ isChecked,
66
68
  actions: i.actions.map((j) => ({ ...j, isChecked })),
67
69
  read_configs: i.read_configs.map((j) => ({ ...j, isChecked })),
68
70
  })),
@@ -80,33 +82,38 @@ const SelectPermission = ({ route }) => {
80
82
  isReadConfig
81
83
  ) => {
82
84
  let data = [...dataStationTemp];
83
- const indexGroup = data.findIndex((item) => item.id === idGroup);
84
- const indexSensor = (data[indexGroup]?.sensors || []).findIndex(
85
- (item) => item.id === sensorId
85
+ const group = data.find((i) => i.id === idGroup);
86
+ const sensor = group.sensors.find((i) => i.id === sensorId);
87
+ const child = sensor[`${isReadConfig ? 'read_configs' : 'actions'}`].find(
88
+ (i) => i.id === childId
86
89
  );
87
- const indexChild = (data[indexGroup]?.sensors || [])[indexSensor][
88
- `${isReadConfig ? 'read_configs' : 'actions'}`
89
- ].findIndex((item) => item.id === childId);
90
- (data[indexGroup]?.sensors || [])[indexSensor][
91
- `${isReadConfig ? 'read_configs' : 'actions'}`
92
- ][indexChild] = {
93
- ...(data[indexGroup]?.sensors || [])[indexSensor][
94
- `${isReadConfig ? 'read_configs' : 'actions'}`
95
- ][indexChild],
96
- isChecked,
97
- };
98
- for (let i of data) {
99
- if (i.sensors.length) {
100
- let isChecked;
101
- let arrChecked = [];
102
- for (let j of i.sensors) {
103
- isChecked = !(
104
- j.actions.some((k) => !k.isChecked) ||
105
- j.read_configs.some((k) => !k.isChecked)
106
- );
107
- arrChecked.push(isChecked);
108
- }
109
- i.isChecked = !arrChecked.some((i) => !i);
90
+
91
+ child.isChecked = isChecked;
92
+ sensor.isChecked = !(
93
+ sensor.read_configs.some((i) => !i.isChecked) ||
94
+ sensor.actions.some((i) => !i.isChecked)
95
+ );
96
+
97
+ for (let station of data) {
98
+ if (station.sensors.length) {
99
+ station.isChecked = !station.sensors.some((i) => !i.isChecked);
100
+ }
101
+ }
102
+ setIsTickAllDevices(!dataStationTemp.some((i) => !i.isChecked));
103
+ dataStationTemp = data;
104
+ setDataStations(data);
105
+ };
106
+
107
+ const onTickedSensor = (idGroup, sensorId, isChecked) => {
108
+ let data = [...dataStationTemp];
109
+ const group = data.find((i) => i.id === idGroup);
110
+ const sensor = group.sensors.find((i) => i.id === sensorId);
111
+
112
+ sensor.isChecked = isChecked;
113
+
114
+ for (let station of data) {
115
+ if (station.sensors.length) {
116
+ station.isChecked = !station.sensors.some((i) => !i.isChecked);
110
117
  }
111
118
  }
112
119
  setIsTickAllDevices(!dataStationTemp.some((i) => !i.isChecked));
@@ -133,6 +140,7 @@ const SelectPermission = ({ route }) => {
133
140
  key={i.id}
134
141
  isRenderSeparated={index !== sensors.length - 1}
135
142
  onTickedChild={onTickedChild}
143
+ onTickedSensor={onTickedSensor}
136
144
  titleGroup={name}
137
145
  activeItemId={activeItemId}
138
146
  setActiveItemId={setActiveItemId}
@@ -147,8 +155,8 @@ const SelectPermission = ({ route }) => {
147
155
  const onPressNext = () => {
148
156
  let read_permissions = [],
149
157
  control_permissions = [];
150
- for (let sensor of dataStationTemp) {
151
- for (let item of sensor.sensors) {
158
+ for (let station of dataStationTemp) {
159
+ for (let item of station.sensors) {
152
160
  let arrIdControlTemp = [],
153
161
  arrIdReadTemp = [];
154
162
  for (let i of item.actions) {
@@ -161,6 +169,10 @@ const SelectPermission = ({ route }) => {
161
169
  control_permissions.push({ id: item.id, values: arrIdControlTemp });
162
170
  arrIdReadTemp.length &&
163
171
  read_permissions.push({ id: item.id, values: arrIdReadTemp });
172
+ !arrIdControlTemp.length &&
173
+ !arrIdReadTemp.length &&
174
+ item.isChecked &&
175
+ read_permissions.push({ id: item.id, values: [] });
164
176
  }
165
177
  }
166
178
  if (!read_permissions.length && !control_permissions.length) {
@@ -21,6 +21,7 @@ import {
21
21
  import { axiosPost } from '../../utils/Apis/axios';
22
22
  import { TESTID } from '../../configs/Constants';
23
23
  import Text from '../../commons/Text';
24
+ import { ToastBottomHelper } from '../../utils/Utils';
24
25
 
25
26
  const SelectUser = ({ route }) => {
26
27
  const t = useTranslations();
@@ -46,9 +47,20 @@ const SelectUser = ({ route }) => {
46
47
  });
47
48
  if (success) {
48
49
  setUsers([...users, data.user]);
50
+ } else {
51
+ const textTemp = t(
52
+ data?.phone
53
+ ? 'text_phone_share_permission'
54
+ : data?.email
55
+ ? 'text_email_share_permission'
56
+ : ''
57
+ );
58
+ ToastBottomHelper.error(
59
+ t('error_share_permission', { data: phone || email, text: textTemp })
60
+ );
49
61
  }
50
62
  },
51
- [unit, permissions, users]
63
+ [users, unit.id, permissions, t]
52
64
  );
53
65
 
54
66
  const validate = useCallback(() => {
@@ -144,6 +144,7 @@ describe('Test SelectPermission', () => {
144
144
  {
145
145
  actions: [{ id: 136, isChecked: true, name: 'action 1' }],
146
146
  id: 123,
147
+ isChecked: true,
147
148
  name: 'child1',
148
149
  read_configs: [{ id: 137, isChecked: true, name: 'config 1' }],
149
150
  },
@@ -165,7 +166,7 @@ describe('Test SelectPermission', () => {
165
166
  const SensorItemElement = instance.findAllByType(SensorItem);
166
167
  expect(SensorItemElement).toHaveLength(1);
167
168
  act(() => {
168
- SensorItemElement[0].props.onTickedChild(204, 123, 136, true, true);
169
+ SensorItemElement[0].props.onTickedChild(204, 123, 137, true, true);
169
170
  });
170
171
  expect(mockSetDataStations).toBeCalled();
171
172
  });
@@ -24,9 +24,11 @@ import { axiosPost, createFormData } from '../../utils/Apis/axios';
24
24
  import { ToastBottomHelper } from '../../utils/Utils';
25
25
  import { TESTID } from '../../configs/Constants';
26
26
  import styles from './AddSubUnitStyles';
27
+ import useKeyboardShow from '../../hooks/Common/useKeyboardShow';
27
28
 
28
29
  const AddSubUnit = ({ route }) => {
29
30
  const t = useTranslations();
31
+ const { dismissKeyboard } = useKeyboardShow();
30
32
  const { navigate, goBack } = useNavigation();
31
33
  const { unit, addType, isAddUnit, location = '' } = route?.params;
32
34
  const [roomName, setRoomName] = useState('');
@@ -126,8 +128,9 @@ const AddSubUnit = ({ route }) => {
126
128
  ]);
127
129
 
128
130
  const onChoosePhoto = useCallback(() => {
131
+ dismissKeyboard();
129
132
  setShowImagePicker(true);
130
- }, []);
133
+ }, [dismissKeyboard]);
131
134
 
132
135
  useEffect(() => {
133
136
  if (imageUrl) {
@@ -4,6 +4,7 @@ import { useTranslations } from '../../hooks/Common/useTranslations';
4
4
  import Routes from '../../utils/Route';
5
5
  import AddSubUnitIcon from '../../../assets/images/Popover/Dashboard/AddSubUnit.svg';
6
6
  import AddDeviceIcon from '../../../assets/images/Popover/Dashboard/AddDevice.svg';
7
+ import SmartAccount from '../../../assets/images/Popover/Dashboard/SmartAccount.svg';
7
8
  import AddMemberIcon from '../../../assets/images/Popover/Dashboard/AddMember.svg';
8
9
  import { useNavigation } from '@react-navigation/native';
9
10
 
@@ -50,6 +51,20 @@ const AddMenu = memo(({ unit, afterItemClick, showAdd, setHideAdd }) => {
50
51
  image: <AddDeviceIcon width={43} height={43} />, // TODO change icon
51
52
  data: { screen: Routes.SetupGatewayWifi, params: { unit_id: unit.id } },
52
53
  },
54
+ {
55
+ id: 5,
56
+ route: Routes.AddGatewayStack,
57
+ text: t('gateway') + ' Wifi',
58
+ image: <AddDeviceIcon width={43} height={43} />,
59
+ data: { screen: Routes.FirstWarning, params: { unit_id: unit.id } },
60
+ },
61
+ {
62
+ id: 6,
63
+ route: Routes.SmartAccount,
64
+ text: t('name_smart_account'),
65
+ image: <SmartAccount width={43} height={43} />, // TODO change icon
66
+ type: Routes.SmartAccount,
67
+ },
53
68
  ];
54
69
  }, [t, unit]);
55
70
 
@@ -31,7 +31,14 @@ import { AUTOMATE_TYPE } from '../../configs/Constants';
31
31
  const UnitDetail = ({ route }) => {
32
32
  const t = useTranslations();
33
33
 
34
- const { unitId, unitData, isOneTap, routeName, isAddSubUnit } = route.params;
34
+ const {
35
+ unitId,
36
+ unitData,
37
+ isOneTap,
38
+ routeName,
39
+ isAddSubUnit,
40
+ isSuccessfullyConnected,
41
+ } = route.params;
35
42
 
36
43
  const isFocused = useIsFocused();
37
44
  const { stateData, setAction } = useContext(SCContext);
@@ -266,7 +273,7 @@ const UnitDetail = ({ route }) => {
266
273
  />
267
274
  );
268
275
  }
269
- if (station.camera_devices) {
276
+ if (station?.camera_devices) {
270
277
  return (
271
278
  <CameraDevice
272
279
  station={station}
@@ -274,7 +281,7 @@ const UnitDetail = ({ route }) => {
274
281
  goToPlayBack={goToPlayBack}
275
282
  />
276
283
  );
277
- } else if (station.isOneTap) {
284
+ } else if (station?.isOneTap) {
278
285
  return (
279
286
  <SubUnitAutomate
280
287
  isOwner={isOwner}
@@ -298,6 +305,10 @@ const UnitDetail = ({ route }) => {
298
305
  navigate(routeName);
299
306
  }, [navigate, routeName]);
300
307
 
308
+ const Dashboard = useCallback(() => {
309
+ navigate(Routes.Dashboard);
310
+ }, [navigate]);
311
+
301
312
  return (
302
313
  <WrapParallaxScrollView
303
314
  uriImg={unit.background}
@@ -310,7 +321,7 @@ const UnitDetail = ({ route }) => {
310
321
  onAdd={setShowAdd}
311
322
  onMore={showPopoverWithRef}
312
323
  hideRightPlus={!isOwner}
313
- onBack={routeName && onBack}
324
+ onBack={(isSuccessfullyConnected && Dashboard) || (routeName && onBack)}
314
325
  >
315
326
  <View style={styles.container}>
316
327
  <Summaries unit={unit} />
@@ -14,9 +14,10 @@ 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 } from '../../hooks/Common';
18
- import { MenuActionMore } from '../../commons';
17
+ import { usePopover, useBoolean } from '../../hooks/Common';
18
+ import { MenuActionMore, AlertAction } from '../../commons';
19
19
  import { useTranslations } from '../../hooks/Common/useTranslations';
20
+ import { useStateAlertRemove } from '../Unit/hook/useStateAlertRemove';
20
21
 
21
22
  const ListSmartAccount = ({ route }) => {
22
23
  const { unitId } = route?.params || {};
@@ -36,6 +37,9 @@ const ListSmartAccount = ({ route }) => {
36
37
 
37
38
  const { childRef, showingPopover, showPopoverWithRef, hidePopover } =
38
39
  usePopover();
40
+ const [lockShowing, acquireLockShowing, releaseLockShowing] = useBoolean();
41
+ const { stateAlertRemove, onShowRemoveAlert, hideAlertAction } =
42
+ useStateAlertRemove();
39
43
 
40
44
  useEffect(() => {
41
45
  getAllSmartAccounts();
@@ -50,29 +54,31 @@ const ListSmartAccount = ({ route }) => {
50
54
  [showPopoverWithRef]
51
55
  );
52
56
 
53
- const deleteSmartAccount = useCallback(async () => {
54
- if (!smartAccountRef?.current) {
55
- return;
56
- }
57
- const id = smartAccountRef.current.id;
58
- const { success } = await axiosDelete(
59
- API.SMART_ACCOUNT.REMOVE_SMART_ACCOUNT(id)
60
- );
61
- success && getAllSmartAccounts();
62
- }, [getAllSmartAccounts]);
63
-
64
57
  const onItemClick = useCallback(
65
58
  (item) => {
66
59
  if (!smartAccountRef?.current) {
67
60
  return;
68
61
  }
69
62
  if (item.action === 'remove') {
70
- deleteSmartAccount();
63
+ acquireLockShowing();
64
+ onShowRemoveAlert(smartAccountRef.current.brand)();
71
65
  }
72
66
  },
73
- [deleteSmartAccount]
67
+ [acquireLockShowing, onShowRemoveAlert]
74
68
  );
75
69
 
70
+ const deleteSmartAccount = useCallback(async () => {
71
+ hideAlertAction();
72
+ if (!smartAccountRef?.current) {
73
+ return;
74
+ }
75
+ const id = smartAccountRef.current.id;
76
+ const { success } = await axiosDelete(
77
+ API.SMART_ACCOUNT.REMOVE_SMART_ACCOUNT(id)
78
+ );
79
+ success && getAllSmartAccounts();
80
+ }, [getAllSmartAccounts, hideAlertAction]);
81
+
76
82
  const listMenuItem = useMemo(() => {
77
83
  return [{ action: 'remove', text: t('remove_account') }];
78
84
  }, [t]);
@@ -88,6 +94,7 @@ const ListSmartAccount = ({ route }) => {
88
94
  },
89
95
  [navigate, unitId]
90
96
  );
97
+
91
98
  return (
92
99
  <View style={styles.wrap}>
93
100
  <WrapHeaderScrollable
@@ -109,12 +116,26 @@ const ListSmartAccount = ({ route }) => {
109
116
  );
110
117
  })}
111
118
  </View>
119
+ <AlertAction
120
+ visible={stateAlertRemove.visible && !lockShowing}
121
+ hideModal={hideAlertAction}
122
+ title={stateAlertRemove.title}
123
+ message={stateAlertRemove.message}
124
+ leftButtonTitle={stateAlertRemove.leftButton}
125
+ leftButtonClick={deleteSmartAccount}
126
+ rightButtonTitle={stateAlertRemove.rightButton}
127
+ rightButtonClick={hideAlertAction}
128
+ boxLeftButtonStyle={styles.boxButtonNotSelect}
129
+ boxRightButtonStyle={styles.boxButtonSelect}
130
+ rightButtonStyle={styles.textRightButton}
131
+ />
112
132
  <MenuActionMore
113
133
  isVisible={showingPopover}
114
134
  hideMore={hidePopover}
115
135
  listMenuItem={listMenuItem}
116
136
  childRef={childRef}
117
137
  onItemClick={onItemClick}
138
+ hideComplete={releaseLockShowing}
118
139
  />
119
140
  </WrapHeaderScrollable>
120
141
  </View>
@@ -42,4 +42,24 @@ export default StyleSheet.create({
42
42
  borderColor: Colors.ShadownTransparent,
43
43
  borderTopWidth: 1,
44
44
  },
45
+ boxButtonNotSelect: {
46
+ marginVertical: 16,
47
+ height: 45,
48
+ borderRadius: 30,
49
+ borderColor: Colors.Primary,
50
+ borderWidth: 1,
51
+ paddingVertical: 0,
52
+ },
53
+ boxButtonSelect: {
54
+ marginVertical: 16,
55
+ height: 45,
56
+ borderRadius: 30,
57
+ borderColor: Colors.Primary,
58
+ backgroundColor: Colors.Primary,
59
+ borderWidth: 1,
60
+ paddingVertical: 0,
61
+ },
62
+ textRightButton: {
63
+ color: Colors.White,
64
+ },
45
65
  });
@@ -1,17 +1,23 @@
1
1
  import React from 'react';
2
2
  import { View } from 'react-native';
3
3
  import { act, create } from 'react-test-renderer';
4
+ import { SCProvider } from '../../../../context';
5
+ import { mockSCStore } from '../../../../context/mockStore';
4
6
  import MyUnitDevice from '../MyUnitDevice';
5
7
 
8
+ const wrapComponent = (sensor) => (
9
+ <SCProvider initState={mockSCStore({})}>
10
+ <MyUnitDevice sensor={sensor} />
11
+ </SCProvider>
12
+ );
13
+
6
14
  describe('Test MyUnitDevice', () => {
7
15
  let tree;
8
16
 
9
17
  it('Test render with status', async () => {
10
18
  await act(() => {
11
19
  tree = create(
12
- <MyUnitDevice
13
- sensor={{ status: 'Ok', name: 'Test', station_name: '' }}
14
- />
20
+ wrapComponent({ status: 'Ok', name: 'Test', station_name: '' })
15
21
  );
16
22
  });
17
23
  const instance = tree.root;
@@ -21,9 +27,7 @@ describe('Test MyUnitDevice', () => {
21
27
 
22
28
  it('Test render without status', async () => {
23
29
  await act(() => {
24
- tree = create(
25
- <MyUnitDevice sensor={{ name: 'Test', station_name: '' }} />
26
- );
30
+ tree = create(wrapComponent({ name: 'Test', station_name: '' }));
27
31
  });
28
32
  const instance = tree.root;
29
33
  const Views = instance.findAllByType(View);