@eohjsc/react-native-smart-city 0.7.21 → 0.7.22

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 (48) hide show
  1. package/package.json +1 -1
  2. package/src/Images/Common/default_end_device.png +0 -0
  3. package/src/commons/Dashboard/MyUnit/index.js +19 -20
  4. package/src/commons/DevMode/Search.js +1 -1
  5. package/src/commons/Device/RainningSensor/CurrentRainSensor.js +5 -5
  6. package/src/commons/Widgets/IFrameWithConfig/IFrameWithConfig.js +1 -3
  7. package/src/commons/Widgets/IFrameWithConfig/__tests__/IFrameWithConfig.test.js +1 -1
  8. package/src/configs/API.js +4 -0
  9. package/src/configs/AccessibilityLabel.js +3 -1
  10. package/src/configs/Images.js +1 -0
  11. package/src/navigations/AddMemberStack.js +3 -3
  12. package/src/screens/AddCommon/SelectUnit.js +3 -2
  13. package/src/screens/AddLocationMaps/__test__/index.test.js +13 -13
  14. package/src/screens/Automate/AddNewAction/__test__/ChooseConfig.test.js +9 -11
  15. package/src/screens/Automate/AddNewAutoSmart/__test__/AddAutomationTypeSmart.test.js +31 -0
  16. package/src/screens/Automate/ScriptDetail/utils.js +0 -20
  17. package/src/screens/ConfirmUnitDeletion/__test__/ConfirmUnitDeletion.test.js +69 -13
  18. package/src/screens/ConfirmUnitDeletion/index.js +14 -14
  19. package/src/screens/Sharing/Components/ConfigItem.js +34 -0
  20. package/src/screens/Sharing/Components/DeviceItem.js +77 -0
  21. package/src/screens/Sharing/Components/ItemChangeRole.js +3 -4
  22. package/src/screens/Sharing/Components/ShareDeviceSelector.js +255 -0
  23. package/src/screens/Sharing/Components/Styles/CheckBoxCustomStyles.js +1 -1
  24. package/src/screens/Sharing/Components/Styles/DeviceItemStyles.js +11 -27
  25. package/src/screens/Sharing/{Styles/SelectPermissionStyles.js → Components/Styles/ShareDeviceSelectorStyles.js} +3 -11
  26. package/src/screens/Sharing/Components/SubUnitItem.js +28 -0
  27. package/src/screens/Sharing/Components/SubUnitTreeView.js +68 -0
  28. package/src/screens/Sharing/Components/TitleCheckBox.js +23 -41
  29. package/src/screens/Sharing/Components/__test__/ItemChangeRole.test.js +7 -7
  30. package/src/screens/Sharing/Components/__test__/ShareDeviceSelector.test.js +298 -0
  31. package/src/screens/Sharing/Components/index.js +14 -1
  32. package/src/screens/Sharing/InfoMemberUnit.js +20 -20
  33. package/src/screens/Sharing/SelectShareDevice.js +11 -255
  34. package/src/screens/Sharing/SelectUser.js +12 -12
  35. package/src/screens/Sharing/UpdateShareDevice.js +45 -301
  36. package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +58 -11
  37. package/src/screens/Sharing/__test__/SelectShareDevice.test.js +51 -160
  38. package/src/screens/Sharing/__test__/SelectUser.test.js +72 -10
  39. package/src/screens/Sharing/__test__/UpdateShareDevice.test.js +49 -209
  40. package/src/utils/I18n/translations/en.js +1 -1
  41. package/src/utils/I18n/translations/vi.js +2 -2
  42. package/src/commons/Sharing/StationDevicePermissions.js +0 -204
  43. package/src/screens/Sharing/Components/CheckBoxConfig.js +0 -44
  44. package/src/screens/Sharing/Components/CheckBoxSubUnit.js +0 -35
  45. package/src/screens/Sharing/Components/EndDevice.js +0 -93
  46. package/src/screens/Sharing/Components/Styles/CheckBoxConfigStyles.js +0 -18
  47. package/src/screens/Sharing/Components/Styles/TitleCheckBoxStyles.js +0 -21
  48. package/src/screens/Sharing/Components/__test__/TitleCheckBox.test.js +0 -31
@@ -0,0 +1,298 @@
1
+ import API from '../../../../configs/API';
2
+ import AccessibilityLabel from '../../../../configs/AccessibilityLabel';
3
+ import { Alert } from 'react-native';
4
+ import ConfigItem from '../ConfigItem';
5
+ import DeviceItem from '../DeviceItem';
6
+ import { IconOutline } from '@ant-design/icons-react-native';
7
+ import MockAdapter from 'axios-mock-adapter';
8
+ import React from 'react';
9
+ import { SCProvider } from '../../../../context';
10
+ import ShareDeviceSelector from '../ShareDeviceSelector';
11
+ import SubUnitItem from '../SubUnitItem';
12
+ import SubUnitTreeView from '../SubUnitTreeView';
13
+ import { ViewButtonBottom } from '../../../../commons';
14
+ import { act } from '@testing-library/react-hooks';
15
+ import api from '../../../../utils/Apis/axios';
16
+ import { create } from 'react-test-renderer';
17
+ import { mockSCStore } from '../../../../context/mockStore';
18
+ import { useNavigation } from '@react-navigation/native';
19
+
20
+ jest.spyOn(Alert, 'alert').mockImplementation(() => {});
21
+ const mock = new MockAdapter(api.axiosInstance);
22
+ const mockOnRightClick = jest.fn();
23
+
24
+ const wrapComponent = (unitId, initKeys = undefined) => (
25
+ <SCProvider initState={mockSCStore({})}>
26
+ <ShareDeviceSelector
27
+ unitId={unitId}
28
+ onRightClick={mockOnRightClick}
29
+ rightTitle={'done'}
30
+ initialSelectedKeys={initKeys}
31
+ />
32
+ </SCProvider>
33
+ );
34
+ describe('test ShareDeviceSelector', () => {
35
+ const unitId = 1;
36
+ let listDevices = [];
37
+ let tree;
38
+ beforeEach(() => {
39
+ mockOnRightClick.mockClear();
40
+ Alert.alert.mockClear();
41
+ listDevices = [
42
+ {
43
+ id: 1,
44
+ name: 'Sub unit 1',
45
+ devices: [
46
+ {
47
+ id: 1,
48
+ icon_kit: 'https://xxx.png',
49
+ actions: [{ id: 1, name: 'action 1' }],
50
+ read_configs: [{ id: 1, name: 'config 1' }],
51
+ name: 'child1',
52
+ },
53
+ {
54
+ id: 2,
55
+ actions: [{ id: 2, name: 'action 2' }],
56
+ read_configs: [{ id: 2, name: 'config 2' }],
57
+ name: 'child2',
58
+ },
59
+ {
60
+ id: 3,
61
+ actions: [],
62
+ read_configs: [],
63
+ name: 'child3',
64
+ },
65
+ ],
66
+ },
67
+ {
68
+ id: 2,
69
+ name: 'Sub unit 2',
70
+ devices: [
71
+ {
72
+ id: 4,
73
+ icon: 'acb.png',
74
+ actions: [{ id: 3, name: 'action 3' }],
75
+ read_configs: [{ id: 3, name: 'config 3' }],
76
+ name: 'child1',
77
+ },
78
+ ],
79
+ },
80
+ ];
81
+ });
82
+
83
+ const clickChooseAllDevices = async (instance, status) => {
84
+ const chooseAllDevices = instance.find(
85
+ (el) =>
86
+ el.props.accessibilityLabel ===
87
+ `${AccessibilityLabel.CHECK_BOX_CUSTOM}-all`
88
+ );
89
+ await act(async () => {
90
+ chooseAllDevices.props.onPress();
91
+ });
92
+ expect(chooseAllDevices.props.isChecked).toEqual(status);
93
+ };
94
+ const clickChooseDevice = async (instance, deviceId, status) => {
95
+ const chooseDevice = instance.find(
96
+ (el) =>
97
+ el.props.accessibilityLabel ===
98
+ `${AccessibilityLabel.SHARE_DEVICE.NAME_END_DEVICE}-device-${deviceId}`
99
+ );
100
+ await act(async () => {
101
+ chooseDevice.props.onPress();
102
+ });
103
+ const iconCheck = instance.findAll(
104
+ (el) =>
105
+ el.props.accessibilityLabel ===
106
+ `${AccessibilityLabel.SHARE_DEVICE.ICON_CHECK}-device-${deviceId}` &&
107
+ el.type === IconOutline
108
+ );
109
+ expect(iconCheck).toHaveLength(status ? 1 : 0);
110
+ };
111
+ const clickChooseConfig = async (instance, configId, status) => {
112
+ const chooseConfig = instance.find(
113
+ (el) =>
114
+ el.props.accessibilityLabel ===
115
+ `${AccessibilityLabel.CHECK_BOX_CUSTOM}-config-${configId}`
116
+ );
117
+ await act(async () => {
118
+ chooseConfig.props.onPress();
119
+ });
120
+ expect(chooseConfig.props.isChecked).toEqual(status);
121
+ };
122
+ const clickChooseAction = async (instance, actionId, status) => {
123
+ const chooseAction = instance.find(
124
+ (el) =>
125
+ el.props.accessibilityLabel ===
126
+ `${AccessibilityLabel.CHECK_BOX_CUSTOM}-action-${actionId}`
127
+ );
128
+ await act(async () => {
129
+ chooseAction.props.onPress();
130
+ });
131
+ expect(chooseAction.props.isChecked).toEqual(status);
132
+ };
133
+ const clickExpandDevice = async (instance, deviceId, status) => {
134
+ const expandDevice = instance.find(
135
+ (el) =>
136
+ el.props.accessibilityLabel ===
137
+ `${AccessibilityLabel.SHARE_DEVICE.EXPAND_END_DEVICE}-device-${deviceId}`
138
+ );
139
+ await act(async () => {
140
+ expandDevice.props.onPress();
141
+ });
142
+ expect(expandDevice.props.name).toEqual(status);
143
+ };
144
+ const clickSubmitSharePermission = async (instance, dataSubmit = null) => {
145
+ const submitSharePermission = instance.findByType(ViewButtonBottom);
146
+ await act(async () => {
147
+ submitSharePermission.props.onRightClick();
148
+ });
149
+ if (!dataSubmit) {
150
+ expect(mockOnRightClick).not.toHaveBeenCalled();
151
+ expect(Alert.alert.mock.calls[0][1]).toEqual(
152
+ 'Please choose at least one device.'
153
+ );
154
+ return;
155
+ }
156
+ expect(mockOnRightClick).toHaveBeenCalledTimes(1);
157
+ expect(mockOnRightClick).toHaveBeenCalledWith(dataSubmit);
158
+ };
159
+ test('render all devices', async () => {
160
+ mock.onGet(API.SHARE.UNIT_PERMISSIONS_v2(unitId)).reply(200, listDevices);
161
+ await act(async () => {
162
+ tree = await create(wrapComponent(unitId));
163
+ });
164
+
165
+ const instance = tree.root;
166
+ expect(instance.findAllByType(ShareDeviceSelector)).toHaveLength(1);
167
+ expect(instance.findAllByType(ViewButtonBottom)).toHaveLength(1);
168
+ expect(instance.findAllByType(SubUnitTreeView)).toHaveLength(1);
169
+ await clickChooseAllDevices(instance, true);
170
+ await clickExpandDevice(instance, 1, 'up');
171
+ await clickExpandDevice(instance, 2, 'up');
172
+ await clickExpandDevice(instance, 4, 'up');
173
+ expect(instance.findAllByType(SubUnitItem)).toHaveLength(3); //All + 2 subunits
174
+ expect(instance.findAllByType(DeviceItem)).toHaveLength(4);
175
+ expect(instance.findAllByType(ConfigItem)).toHaveLength(6);
176
+ const dataSubmit = {
177
+ read_permissions: [
178
+ { id: 1, values: [1] },
179
+ { id: 2, values: [2] },
180
+ { id: 3, values: [] },
181
+ { id: 4, values: [3] },
182
+ ],
183
+ control_permissions: [
184
+ { id: 1, values: [1] },
185
+ { id: 2, values: [2] },
186
+ { id: 4, values: [3] },
187
+ ],
188
+ };
189
+ await clickSubmitSharePermission(instance, dataSubmit);
190
+ });
191
+ test('render empty subunit', async () => {
192
+ mock.onGet(API.SHARE.UNIT_PERMISSIONS_v2(unitId)).reply(200, []);
193
+ await act(async () => {
194
+ tree = await create(wrapComponent(unitId));
195
+ });
196
+ const instance = tree.root;
197
+ const noDataText = instance.find(
198
+ (el) =>
199
+ el.props.accessibilityLabel === AccessibilityLabel.TEXT_NO_DATA_STATIONS
200
+ );
201
+ expect(noDataText).toBeDefined();
202
+ expect(instance.findAllByType(SubUnitItem)).toHaveLength(0);
203
+ expect(instance.findAllByType(DeviceItem)).toHaveLength(0);
204
+ expect(instance.findAllByType(ConfigItem)).toHaveLength(0);
205
+ await clickSubmitSharePermission(instance);
206
+ });
207
+ test('click choose end device and config', async () => {
208
+ mock.onGet(API.SHARE.UNIT_PERMISSIONS_v2(unitId)).reply(200, listDevices);
209
+ await act(async () => {
210
+ tree = await create(wrapComponent(unitId));
211
+ });
212
+ const instance = tree.root;
213
+ expect(instance.findAllByType(SubUnitItem)).toHaveLength(3); //All + 2 subunits
214
+ expect(instance.findAllByType(DeviceItem)).toHaveLength(4);
215
+ await clickChooseDevice(instance, 1, true);
216
+ await clickExpandDevice(instance, 1, 'up');
217
+ expect(instance.findAllByType(ConfigItem)).toHaveLength(2);
218
+ const dataSubmit = {
219
+ read_permissions: [{ id: 1, values: [1] }],
220
+ control_permissions: [{ id: 1, values: [1] }],
221
+ };
222
+ await clickSubmitSharePermission(instance, dataSubmit);
223
+ });
224
+ test('render with init keys and go back', async () => {
225
+ mock.onGet(API.SHARE.UNIT_PERMISSIONS_v2(unitId)).reply(200, listDevices);
226
+ await act(async () => {
227
+ tree = await create(wrapComponent(unitId, ['config-3', 'action-3']));
228
+ });
229
+ const instance = tree.root;
230
+ expect(instance.findAllByType(SubUnitItem)).toHaveLength(3); //All + 2 subunits
231
+ expect(instance.findAllByType(DeviceItem)).toHaveLength(4);
232
+ const iconCheck = instance.findAll(
233
+ (el) =>
234
+ el.props.accessibilityLabel ===
235
+ `${AccessibilityLabel.SHARE_DEVICE.ICON_CHECK}-device-4` &&
236
+ el.type === IconOutline
237
+ );
238
+ expect(iconCheck).toHaveLength(1);
239
+ await clickExpandDevice(instance, 4, 'up');
240
+ const config = instance.find(
241
+ (el) =>
242
+ el.props.accessibilityLabel ===
243
+ `${AccessibilityLabel.CHECK_BOX_CUSTOM}-config-3`
244
+ );
245
+ const action = instance.find(
246
+ (el) =>
247
+ el.props.accessibilityLabel ===
248
+ `${AccessibilityLabel.CHECK_BOX_CUSTOM}-action-3`
249
+ );
250
+ expect(config.props.isChecked).toEqual(true);
251
+ expect(action.props.isChecked).toEqual(true);
252
+ await clickChooseDevice(instance, 4, false);
253
+
254
+ const mockGoBack = useNavigation().goBack;
255
+ const viewButtonBottom = instance.findByType(ViewButtonBottom);
256
+ await act(() => {
257
+ viewButtonBottom.props.onLeftClick();
258
+ });
259
+ expect(mockGoBack).toHaveBeenCalledTimes(1);
260
+ });
261
+ test('test 1 config id in 2 device', async () => {
262
+ listDevices[0].devices[1].actions.push({ id: 3, name: 'action-3' });
263
+ mock.onGet(API.SHARE.UNIT_PERMISSIONS_v2(unitId)).reply(200, listDevices);
264
+ await act(async () => {
265
+ tree = await create(wrapComponent(unitId));
266
+ });
267
+ const instance = tree.root;
268
+ expect(instance.findAllByType(SubUnitItem)).toHaveLength(3); //All + 2 subunits
269
+ expect(instance.findAllByType(DeviceItem)).toHaveLength(4);
270
+
271
+ await clickExpandDevice(instance, 1, 'up');
272
+ await clickChooseConfig(instance, 1, true);
273
+ await clickChooseAction(instance, 1, true);
274
+
275
+ await clickExpandDevice(instance, 2, 'up');
276
+ await clickExpandDevice(instance, 4, 'up');
277
+
278
+ const actions = instance.findAll(
279
+ (el) =>
280
+ el.props.accessibilityLabel ===
281
+ `${AccessibilityLabel.CHECK_BOX_CUSTOM}-action-3`
282
+ );
283
+ expect(actions).toHaveLength(2);
284
+ await act(async () => {
285
+ actions[0].props.onPress();
286
+ });
287
+ expect(actions[0].props.isChecked).toEqual(true);
288
+
289
+ const dataSubmit = {
290
+ read_permissions: [{ id: 1, values: [1] }],
291
+ control_permissions: [
292
+ { id: 1, values: [1] },
293
+ { id: 4, values: [3] },
294
+ ],
295
+ };
296
+ await clickSubmitSharePermission(instance, dataSubmit);
297
+ });
298
+ });
@@ -1,4 +1,17 @@
1
1
  import CheckBoxCustom from './CheckBoxCustom';
2
+ import ConfigItem from './ConfigItem';
3
+ import DeviceItem from './DeviceItem';
4
+ import ShareDeviceSelector from './ShareDeviceSelector';
5
+ import SubUnitItem from './SubUnitItem';
6
+ import SubUnitTreeView from './SubUnitTreeView';
2
7
  import TitleCheckBox from './TitleCheckBox';
3
8
 
4
- export { CheckBoxCustom, TitleCheckBox };
9
+ export {
10
+ CheckBoxCustom,
11
+ TitleCheckBox,
12
+ ConfigItem,
13
+ DeviceItem,
14
+ SubUnitItem,
15
+ SubUnitTreeView,
16
+ ShareDeviceSelector,
17
+ };
@@ -1,32 +1,32 @@
1
- import React, { useState, useCallback, useEffect, memo, useMemo } from 'react';
2
- import { View, ActivityIndicator, Image, TouchableOpacity } from 'react-native';
3
- import { useTranslations } from '../../hooks/Common/useTranslations';
1
+ import { API, Colors, Images, SCConfig } from '../../configs';
2
+ import { ActivityIndicator, Image, TouchableOpacity, View } from 'react-native';
3
+ import { AlertAction, ViewButtonBottom } from '../../commons';
4
+ import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
5
+ import { useDataMember, useStateAlertAction } from './hooks';
6
+ import { useIsFocused, useNavigation } from '@react-navigation/native';
7
+
8
+ import { AccessibilityLabel } from '../../configs/Constants';
4
9
  import { CircleView } from '../../commons/CircleView';
5
- import Text from '../../commons/Text';
6
- import { shortEmailName } from '../../utils/Utils';
7
- import { Colors, Images, API, SCConfig } from '../../configs';
8
10
  import { HeaderCustom } from '../../commons/Header';
9
- import RowMemberInfo from '../GuestInfo/components/RowGuestInfo';
10
- import { useIsOwnerOfUnit } from '../../hooks/Common';
11
- import { axiosGet } from '../../utils/Apis/axios';
12
- import { AlertAction, ViewButtonBottom } from '../../commons';
13
- import { useStateAlertAction, useDataMember } from './hooks';
14
11
  import ItemChangeRole from './Components/ItemChangeRole';
15
12
  import MemberSvg from '../../Images/Common/member.svg';
13
+ import ModalPopupCT from '../../commons/ModalPopupCT';
16
14
  import OwnerSvg from '../../Images/Common/owner.svg';
17
- import styles from './Styles/inforMemberUnitStyles';
18
- import { useNavigation, useIsFocused } from '@react-navigation/native';
19
15
  import Routes from '../../utils/Route';
20
- import { AccessibilityLabel } from '../../configs/Constants';
21
- import ModalPopupCT from '../../commons/ModalPopupCT';
16
+ import RowMemberInfo from '../GuestInfo/components/RowGuestInfo';
17
+ import Text from '../../commons/Text';
18
+ import { axiosGet } from '../../utils/Apis/axios';
19
+ import { shortEmailName } from '../../utils/Utils';
20
+ import styles from './Styles/inforMemberUnitStyles';
21
+ import { useIsOwnerOfUnit } from '../../hooks/Common';
22
+ import { useTranslations } from '../../hooks/Common/useTranslations';
22
23
 
23
24
  const InfoMemberUnit = memo(({ route }) => {
24
25
  const t = useTranslations();
25
- const { member, unit } = route?.params ||
26
- {} || {
27
- member: {},
28
- unit: {},
29
- };
26
+ const { member, unit } = route?.params || {
27
+ member: {},
28
+ unit: {},
29
+ };
30
30
  const { navigate } = useNavigation();
31
31
  const [isLoading, setIsLoading] = useState(true);
32
32
  const [isShowWarning, setIsShowWarning] = useState(false);
@@ -1,232 +1,15 @@
1
- import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
2
- import { View, FlatList, Alert, ActivityIndicator } from 'react-native';
1
+ import React from 'react';
2
+ import Routes from '../../utils/Route';
3
+ import { ShareDeviceSelector } from './Components';
3
4
  import { useNavigation } from '@react-navigation/native';
4
-
5
5
  import { useTranslations } from '../../hooks/Common/useTranslations';
6
- import ViewButtonBottom from '../../commons/ViewButtonBottom';
7
- import Text from '../../commons/Text';
8
- import styles from './Styles/SelectPermissionStyles';
9
- import { axiosGet } from '../../utils/Apis/axios';
10
- import Routes from '../../utils/Route';
11
- import { API, Colors } from '../../configs';
12
- import { AccessibilityLabel } from '../../configs/Constants';
13
- import CheckBoxSubUnit from './Components/CheckBoxSubUnit';
14
- import EndDevice from './Components/EndDevice';
15
6
 
16
7
  const SelectShareDevice = ({ route }) => {
17
8
  const { unit } = route.params;
18
- const t = useTranslations();
19
9
  const navigation = useNavigation();
20
- const [dataStations, setDataStations] = useState([]);
21
- const [isTickAllDevices, setIsTickAllDevices] = useState(false);
22
- const [expandedItemIds, setExpandedItemIds] = useState([]);
23
- const [needRefresh, setNeedRefresh] = useState(false);
24
- const [loading, setLoading] = useState(true);
25
-
26
- const onTickAllDevices = (indexSubUnit, isChecked) => {
27
- setIsTickAllDevices(isChecked);
28
- const updatedDataStations = dataStations.map((station) => ({
29
- ...station,
30
- isChecked,
31
- devices: station.devices.map((device) => ({
32
- ...device,
33
- isChecked,
34
- actions: device.actions.map((action) => ({
35
- ...action,
36
- isChecked,
37
- })),
38
- read_configs: device.read_configs.map((config) => ({
39
- ...config,
40
- isChecked,
41
- })),
42
- })),
43
- }));
44
- setDataStations(updatedDataStations);
45
- };
46
-
47
- const onTickSubUnit = (indexSubUnit, isChecked) => {
48
- dataStations[indexSubUnit] = {
49
- ...dataStations[indexSubUnit],
50
- isChecked,
51
- devices: dataStations[indexSubUnit].devices.map((i) => ({
52
- ...i,
53
- isChecked,
54
- actions: i.actions.map((j) => ({ ...j, isChecked })),
55
- read_configs: i.read_configs.map((j) => ({ ...j, isChecked })),
56
- })),
57
- };
58
- setDataStations(dataStations);
59
- setIsTickAllDevices(!dataStations.some((object) => !object.isChecked));
60
- setNeedRefresh(true);
61
- };
62
-
63
- const onTickEndDevice = (indexSubUnit, indexEndDevice, item, isChecked) => {
64
- item.isChecked = isChecked;
65
- item.actions = item.actions.map((j) => ({ ...j, isChecked }));
66
- item.read_configs = item.read_configs.map((j) => ({ ...j, isChecked }));
67
-
68
- dataStations[indexSubUnit].devices[indexEndDevice] = item;
69
-
70
- dataStations[indexSubUnit] = {
71
- ...dataStations[indexSubUnit],
72
- isChecked:
73
- dataStations[indexSubUnit].devices.length ===
74
- dataStations[indexSubUnit].devices.filter((i) => i.isChecked).length,
75
- };
76
- setIsTickAllDevices(!dataStations.some((i) => !i.isChecked));
77
- setDataStations(dataStations);
78
- setNeedRefresh(true);
79
- };
80
-
81
- const onTickedChild = (
82
- indexSubUnit,
83
- indexEndDevice,
84
- configId,
85
- item,
86
- isConfig,
87
- isChecked
88
- ) => {
89
- const subUnit = dataStations[indexSubUnit];
90
- const device = subUnit.devices[indexEndDevice];
91
- const configs = item[`${isConfig ? 'read_configs' : 'actions'}`];
92
- const child = configs.find((i) => i.id === configId);
93
-
94
- child.isChecked = isChecked;
95
- device.isChecked =
96
- configs.length === configs.filter((i) => i.isChecked).length;
97
-
98
- subUnit.isChecked =
99
- subUnit.devices.length ===
100
- subUnit.devices.filter((i) => i.isChecked).length;
101
-
102
- setIsTickAllDevices(!dataStations.some((i) => !i.isChecked));
103
- setDataStations(dataStations);
104
- setNeedRefresh(true);
105
- };
106
-
107
- const toggleExpandEndDevice = (deviceItem) => () => {
108
- setExpandedItemIds((ids) => {
109
- const index = ids.indexOf(deviceItem.id);
110
- if (index !== -1) {
111
- return [...ids.slice(0, index), ...ids.slice(index + 1)];
112
- } else {
113
- return [...ids, deviceItem.id];
114
- }
115
- });
116
- };
117
-
118
- const expandEndDevice = (deviceItem) => () => {
119
- if (!expandedItemIds.includes(deviceItem.id)) {
120
- setExpandedItemIds((prev) => [...prev, deviceItem.id]);
121
- }
122
- };
123
-
124
- const GroupEndDevice = ({ item, indexSubUnit }) => {
125
- const { name, devices, isChecked } = item;
126
- return (
127
- <View style={styles.viewGroup}>
128
- <CheckBoxSubUnit
129
- title={name}
130
- item={item}
131
- onPress={onTickSubUnit}
132
- isChecked={isChecked}
133
- indexSubUnit={indexSubUnit}
134
- />
135
- <View style={styles.wrapDevice}>
136
- {devices.map((i, index) => (
137
- <EndDevice
138
- item={i}
139
- key={i.id}
140
- onTickedChild={onTickedChild}
141
- onTickEndDevice={onTickEndDevice}
142
- isItemExpanded={expandedItemIds.includes(i.id)}
143
- toggleExpandEndDevice={toggleExpandEndDevice(i)}
144
- expandEndDevice={expandEndDevice(i)}
145
- indexSubUnit={indexSubUnit}
146
- indexEndDevice={index}
147
- />
148
- ))}
149
- </View>
150
- </View>
151
- );
152
- };
153
-
154
- const renderSubUnit = ({ item, index }) => (
155
- <GroupEndDevice key={item.id} item={item} indexSubUnit={index} />
156
- );
157
-
158
- const renderFlatList = useMemo(() => {
159
- setNeedRefresh(false);
160
- return (
161
- <FlatList
162
- keyExtractor={(item) => item.id}
163
- extraData={dataStations}
164
- data={dataStations}
165
- renderItem={renderSubUnit}
166
- scrollIndicatorInsets={{ right: 1 }}
167
- ListHeaderComponent={
168
- <CheckBoxSubUnit
169
- title={t('text_all_devices')}
170
- onPress={onTickAllDevices}
171
- isChecked={isTickAllDevices}
172
- />
173
- }
174
- />
175
- );
176
- // eslint-disable-next-line react-hooks/exhaustive-deps
177
- }, [dataStations, isTickAllDevices, expandedItemIds, needRefresh]);
178
-
179
- const getUnitPermission = useCallback(async () => {
180
- const { success, data } = await axiosGet(
181
- API.SHARE.UNIT_PERMISSIONS(unit.id)
182
- );
183
- if (success) {
184
- setDataStations(data);
185
- }
186
- setLoading(false);
187
- }, [unit.id]);
188
-
189
- useEffect(() => {
190
- getUnitPermission();
191
- }, [getUnitPermission, unit]);
192
-
193
- const onPressBottom = async () => {
194
- setLoading(true);
195
- let read_permissions = [];
196
- let control_permissions = [];
197
-
198
- for (const station of dataStations) {
199
- for (const end_device of station.devices) {
200
- const action_ids = end_device.actions
201
- .filter((action) => action.isChecked)
202
- .map((action) => action.id);
203
-
204
- const config_ids = end_device.read_configs
205
- .filter((config) => config.isChecked)
206
- .map((config) => config.id);
207
-
208
- if (action_ids.length > 0) {
209
- control_permissions.push({ id: end_device.id, values: action_ids });
210
- }
211
-
212
- if (config_ids.length > 0) {
213
- read_permissions.push({ id: end_device.id, values: config_ids });
214
- }
10
+ const t = useTranslations();
215
11
 
216
- if (
217
- action_ids.length === 0 &&
218
- config_ids.length === 0 &&
219
- end_device.isChecked
220
- ) {
221
- read_permissions.push({ id: end_device.id, values: [] });
222
- }
223
- }
224
- }
225
- setLoading(false);
226
- if (read_permissions.length === 0 && control_permissions.length === 0) {
227
- Alert.alert('', t('choose_at_least_one'));
228
- return;
229
- }
12
+ const handleOnRightClick = ({ read_permissions, control_permissions }) => {
230
13
  navigation.navigate(Routes.SharingInviteMembers, {
231
14
  unit,
232
15
  permissions: { read_permissions, control_permissions },
@@ -234,39 +17,12 @@ const SelectShareDevice = ({ route }) => {
234
17
  };
235
18
 
236
19
  return (
237
- <View style={styles.wrap}>
238
- <Text semibold style={styles.title}>
239
- {t('select_device')}
240
- </Text>
241
- <Text style={styles.subtitle}>{t('sharing_select_devices_hint')}</Text>
242
- <View style={styles.contentContainer}>
243
- {loading ? (
244
- <ActivityIndicator color={Colors.Primary} />
245
- ) : dataStations.length > 0 ? (
246
- renderFlatList
247
- ) : (
248
- <Text
249
- style={styles.textNodata}
250
- accessibilityLabel={AccessibilityLabel.TEXT_NO_DATA_STATIONS}
251
- >
252
- {t('no_data')}
253
- </Text>
254
- )}
255
- </View>
256
- <View style={styles.wrapViewButtonStyle}>
257
- <ViewButtonBottom
258
- accessibilityLabelPrefix={
259
- AccessibilityLabel.PREFIX.SHARING_SELECT_PERMISSION
260
- }
261
- leftTitle={t('cancel')}
262
- onLeftClick={() => navigation.goBack()}
263
- rightTitle={t('next')}
264
- rightDisabled={false}
265
- onRightClick={onPressBottom}
266
- />
267
- </View>
268
- </View>
20
+ <ShareDeviceSelector
21
+ unitId={unit.id}
22
+ onRightClick={handleOnRightClick}
23
+ rightTitle={t('next')}
24
+ />
269
25
  );
270
26
  };
271
27
 
272
- export default memo(SelectShareDevice);
28
+ export default SelectShareDevice;