@eohjsc/react-native-smart-city 0.3.33 → 0.3.36

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 (96) hide show
  1. package/assets/images/AddNewDevice/add-gateway-icon.svg +13 -0
  2. package/assets/images/AddNewDevice/add-modbus-device-icon.svg +8 -0
  3. package/assets/images/AddNewDevice/add-wifi-device-icon.svg +4 -0
  4. package/assets/images/AddNewDevice/add-zigbee-device-icon.svg +4 -0
  5. package/assets/images/AddNewDevice/interact-smartphone-icon.svg +14 -0
  6. package/assets/images/AddNewDevice/on-icon.svg +5 -0
  7. package/assets/images/AddNewDevice/router-connect-icon.svg +3 -0
  8. package/index.js +2 -0
  9. package/package.json +2 -1
  10. package/src/Images/SmartAccount/DienQuang.png +0 -0
  11. package/src/Images/SmartAccount/DienQuang@2x.png +0 -0
  12. package/src/Images/SmartAccount/DienQuang@3x.png +0 -0
  13. package/src/Images/SmartAccount/LG.png +0 -0
  14. package/src/Images/SmartAccount/LG@2x.png +0 -0
  15. package/src/Images/SmartAccount/LG@3x.png +0 -0
  16. package/src/Images/SmartAccount/Samsung.png +0 -0
  17. package/src/Images/SmartAccount/Samsung@2x.png +0 -0
  18. package/src/Images/SmartAccount/Samsung@3x.png +0 -0
  19. package/src/commons/Device/HorizontalBarChart.js +2 -6
  20. package/src/commons/SelectSubUnit/index.js +18 -18
  21. package/src/commons/SelectSubUnit/styles.js +0 -3
  22. package/src/commons/SelectUnit/index.js +2 -3
  23. package/src/commons/SelectUnit/styles.js +1 -2
  24. package/src/configs/API.js +10 -0
  25. package/src/configs/AccessibilityLabel.js +18 -0
  26. package/src/configs/Constants.js +2 -0
  27. package/src/configs/Images.js +2 -0
  28. package/src/context/actionType.ts +3 -0
  29. package/src/context/reducer.ts +22 -0
  30. package/src/navigations/AddGatewayStack.js +15 -0
  31. package/src/navigations/SmartAccountStack.js +52 -0
  32. package/src/screens/AddCommon/SelectSubUnit.js +17 -13
  33. package/src/screens/AddCommon/SelectUnit.js +14 -7
  34. package/src/screens/AddCommon/__test__/SelectSubUnit.test.js +18 -11
  35. package/src/screens/AddCommon/__test__/SelectUnit.test.js +13 -6
  36. package/src/screens/AddNewAction/SetupSensor.js +6 -0
  37. package/src/screens/AddNewGateway/ConnectingModbusDevice.js +0 -1
  38. package/src/screens/AddNewGateway/ConnectingWifiGuide.js +167 -0
  39. package/src/screens/AddNewGateway/ConnectingWifiGuideStyles.js +58 -0
  40. package/src/screens/AddNewGateway/PlugAndPlay/ConnectRouterGuide.js +49 -0
  41. package/src/screens/AddNewGateway/PlugAndPlay/ConnectRouterGuideStyles.js +31 -0
  42. package/src/screens/AddNewGateway/PlugAndPlay/FirstWarning.js +0 -5
  43. package/src/screens/AddNewGateway/PlugAndPlay/ZigbeeDeviceConnectGuide.js +51 -0
  44. package/src/screens/AddNewGateway/PlugAndPlay/__test__/FirstWarning.test.js +1 -1
  45. package/src/screens/AddNewGateway/ScanGatewayQR.js +23 -12
  46. package/src/screens/AddNewGateway/ScanModbusQR.js +75 -11
  47. package/src/screens/AddNewGateway/ScanWifiDeviceQR.js +19 -9
  48. package/src/screens/AddNewGateway/SelectDeviceSubUnit.js +26 -7
  49. package/src/screens/AddNewGateway/SelectDeviceType.js +145 -43
  50. package/src/screens/AddNewGateway/SelectDeviceTypeStyles.js +51 -0
  51. package/src/screens/AddNewGateway/SelectDeviceUnit.js +1 -1
  52. package/src/screens/AddNewGateway/SelectZigbeeGateway.js +1 -1
  53. package/src/screens/AddNewGateway/ShareWifiPassword.js +7 -112
  54. package/src/screens/AddNewGateway/__test__/ConnectingWifiGuide.test.js +224 -0
  55. package/src/screens/AddNewGateway/__test__/ScanGatewayQR.test.js +6 -7
  56. package/src/screens/AddNewGateway/__test__/ScanModbusQR.test.js +4 -6
  57. package/src/screens/AddNewGateway/__test__/ScanWifiDeviceQR.test.js +5 -7
  58. package/src/screens/AddNewGateway/__test__/SelectDeviceSubUnit.test.js +2 -1
  59. package/src/screens/AddNewGateway/__test__/SelectDeviceType.test.js +46 -11
  60. package/src/screens/AddNewGateway/__test__/SelectZigbeeGateway.test.js +1 -1
  61. package/src/screens/AddNewGateway/__test__/ShareWifiPassword.test.js +4 -143
  62. package/src/screens/EditActionsList/__tests__/index.test.js +120 -0
  63. package/src/screens/EditActionsList/index.js +15 -5
  64. package/src/screens/ScanChipQR/components/InvalidQRCode/index.js +33 -0
  65. package/src/screens/ScanChipQR/components/QRScan/index.js +7 -5
  66. package/src/screens/SharedUnit/__test__/ShareUnit.test.js +25 -0
  67. package/src/screens/SharedUnit/index.js +2 -0
  68. package/src/screens/Sharing/Components/DeviceItem.js +24 -8
  69. package/src/screens/Sharing/SelectPermission.js +78 -62
  70. package/src/screens/Sharing/__test__/SelectPermission.test.js +55 -2
  71. package/src/screens/SmartAccount/Connecting/index.js +171 -0
  72. package/src/screens/SmartAccount/Connecting/style.js +50 -0
  73. package/src/screens/SmartAccount/ListDevice/DeviceItem.js +45 -0
  74. package/src/screens/SmartAccount/ListDevice/__test__/DeviceItem.test.js +34 -0
  75. package/src/screens/SmartAccount/ListDevice/__test__/ListDevice.test.js +139 -0
  76. package/src/screens/SmartAccount/ListDevice/index.js +185 -0
  77. package/src/screens/SmartAccount/ListDevice/styles/DeviceItemStyles.js +70 -0
  78. package/src/screens/SmartAccount/ListDevice/styles/index.js +85 -0
  79. package/src/screens/SmartAccount/SuccessfullyConnected/DeviceItem.js +37 -0
  80. package/src/screens/SmartAccount/SuccessfullyConnected/DeviceItemStyles.js +49 -0
  81. package/src/screens/SmartAccount/SuccessfullyConnected/__test__/DeviceItem.test.js +65 -0
  82. package/src/screens/SmartAccount/SuccessfullyConnected/__test__/SuccessfullyConnected.test.js +88 -0
  83. package/src/screens/SmartAccount/SuccessfullyConnected/index.js +101 -0
  84. package/src/screens/SmartAccount/SuccessfullyConnected/styles.js +42 -0
  85. package/src/screens/SmartAccount/__test__/Connecting.test.js +86 -0
  86. package/src/screens/SmartAccount/__test__/SmartAccount.test.js +249 -0
  87. package/src/screens/SmartAccount/index.js +172 -0
  88. package/src/screens/SmartAccount/style.js +70 -0
  89. package/src/screens/SyncLGDevice/__test__/AddLGDevice.test.js +37 -10
  90. package/src/screens/Unit/AddMenu.js +4 -5
  91. package/src/screens/Unit/SmartAccount.js +8 -5
  92. package/src/screens/Unit/__test__/SmartAccount.test.js +8 -5
  93. package/src/utils/I18n/translations/en.json +35 -2
  94. package/src/utils/I18n/translations/vi.json +36 -2
  95. package/src/utils/Route/index.js +6 -0
  96. package/src/utils/Validation.js +4 -0
@@ -0,0 +1,120 @@
1
+ import React from 'react';
2
+ import DraggableFlatList from 'react-native-draggable-flatlist';
3
+ import { create, act } from 'react-test-renderer';
4
+ import { SCProvider } from '../../../context';
5
+ import { mockSCStore } from '../../../context/mockStore';
6
+ import EditActionsList from '../index';
7
+ import { AccessibilityLabel } from '../../../configs/Constants';
8
+ import { TouchableOpacity } from 'react-native';
9
+ import api from '../../../utils/Apis/axios';
10
+ import MockAdapter from 'axios-mock-adapter';
11
+ import API from '../../../configs/API';
12
+
13
+ import { ModalBottom } from '../../../commons/Modal';
14
+
15
+ const mockSetdata = jest.fn();
16
+ const mockedNavigate = jest.fn();
17
+ const mockGoBack = jest.fn();
18
+
19
+ const wrapComponent = () => (
20
+ <SCProvider initState={mockSCStore({})}>
21
+ <EditActionsList />
22
+ </SCProvider>
23
+ );
24
+ const mock = new MockAdapter(api.axiosInstance);
25
+
26
+ jest.mock('@react-navigation/native', () => {
27
+ return {
28
+ ...jest.requireActual('@react-navigation/native'),
29
+ useRoute: jest.fn().mockReturnValue({
30
+ params: {
31
+ data: [{ id: 1, sensor_name: 'abc', station_name: 'abc' }],
32
+ id: 1,
33
+ setData: mockSetdata,
34
+ unit: { id: 1, name: 'unit' },
35
+ },
36
+ }),
37
+ useNavigation: () => ({
38
+ navigate: mockedNavigate,
39
+ goBack: mockGoBack,
40
+ }),
41
+ useIsFocused: () => true,
42
+ };
43
+ });
44
+
45
+ jest.mock('react-native-parsed-text', () => () => []);
46
+
47
+ describe('EditActionsList', () => {
48
+ let tree;
49
+
50
+ beforeEach(() => {
51
+ mockGoBack.mockClear();
52
+ });
53
+ test('render EditActionsList', async () => {
54
+ await act(async () => {
55
+ tree = await create(wrapComponent());
56
+ });
57
+ const instance = tree.root;
58
+ const draggableFlatList = instance.findAllByType(DraggableFlatList);
59
+ const modalBottom = instance.findAllByType(ModalBottom);
60
+ expect(draggableFlatList).toHaveLength(1);
61
+ expect(modalBottom).toHaveLength(1);
62
+ });
63
+ test('EditActionsList onPress cancel', async () => {
64
+ await act(async () => {
65
+ tree = await create(wrapComponent());
66
+ });
67
+ const instance = tree.root;
68
+ const buttonCancel = instance.find(
69
+ (el) =>
70
+ el.props.accessibilityLabel ===
71
+ AccessibilityLabel.BUTTON_CANCEL_EDIT_ACTION_LIST &&
72
+ el.type === TouchableOpacity
73
+ );
74
+ expect(buttonCancel).toBeDefined();
75
+ buttonCancel.props.onPress();
76
+ expect(mockGoBack).toHaveBeenCalled();
77
+ });
78
+ test('EditActionsList onPress save', async () => {
79
+ mock.onPut(API.AUTOMATE.ORDER_SCRIPT_ACTION(1)).reply(200);
80
+ await act(async () => {
81
+ tree = await create(wrapComponent());
82
+ });
83
+ const instance = tree.root;
84
+ const buttonSave = instance.find(
85
+ (el) =>
86
+ el.props.accessibilityLabel ===
87
+ AccessibilityLabel.BUTTON_SAVE_EDIT_ACTION_LIST &&
88
+ el.type === TouchableOpacity
89
+ );
90
+ expect(buttonSave).toBeDefined();
91
+ buttonSave.props.onPress();
92
+ expect(mock.history.put).toHaveLength(1);
93
+ });
94
+ test('EditActionsList onPress remove', async () => {
95
+ await act(async () => {
96
+ tree = await create(wrapComponent());
97
+ });
98
+ const instance = tree.root;
99
+ const buttonRemove = instance.find(
100
+ (el) =>
101
+ el.props.accessibilityLabel ===
102
+ AccessibilityLabel.BUTTON_REMOVE_EDIT_ACTION_LIST &&
103
+ el.type === TouchableOpacity
104
+ );
105
+ expect(buttonRemove).toBeDefined();
106
+ buttonRemove.props.onPress();
107
+ });
108
+ test('EditActionsList modal onPress remove', async () => {
109
+ await act(async () => {
110
+ tree = await create(wrapComponent());
111
+ });
112
+ const instance = tree.root;
113
+ const modalBottom = instance.findAllByType(ModalBottom);
114
+ modalBottom[0].props.onRemove();
115
+
116
+ mock.onDelete(API.AUTOMATE.DELETE_SCRIPT_ACTION(1, 1)).reply(200);
117
+ expect(mock.history.delete).toHaveLength(1);
118
+ modalBottom[0].props.onClose();
119
+ });
120
+ });
@@ -1,4 +1,4 @@
1
- import React, { useState, useCallback } from 'react';
1
+ import React, { memo, useState, useCallback } from 'react';
2
2
  import { View, TouchableOpacity } from 'react-native';
3
3
  import DraggableFlatList from 'react-native-draggable-flatlist';
4
4
  import { useNavigation, useRoute } from '@react-navigation/native';
@@ -15,8 +15,9 @@ import Close from '../../../assets/images/Close.svg';
15
15
  import { axiosDelete, axiosPut } from '../../utils/Apis/axios';
16
16
  import { ModalBottom } from '../../commons/Modal';
17
17
  import { ToastBottomHelper } from '../../utils/Utils';
18
+ import { AccessibilityLabel } from '../../configs/Constants';
18
19
 
19
- const EditActionsList = () => {
20
+ const EditActionsList = memo(() => {
20
21
  const t = useTranslations();
21
22
  const { goBack } = useNavigation();
22
23
  const { params = {} } = useRoute() || {};
@@ -115,6 +116,9 @@ const EditActionsList = () => {
115
116
  </Text>
116
117
  </View>
117
118
  <TouchableOpacity
119
+ accessibilityLabel={
120
+ AccessibilityLabel.BUTTON_REMOVE_EDIT_ACTION_LIST
121
+ }
118
122
  onPress={onPressRemove({
119
123
  id: item?.id,
120
124
  actionName: item?.sensor_name,
@@ -148,12 +152,18 @@ const EditActionsList = () => {
148
152
  />
149
153
  </View>
150
154
  <View style={styles.wrapBottom}>
151
- <TouchableOpacity onPress={onPressCancel}>
155
+ <TouchableOpacity
156
+ onPress={onPressCancel}
157
+ accessibilityLabel={AccessibilityLabel.BUTTON_CANCEL_EDIT_ACTION_LIST}
158
+ >
152
159
  <Text type="H4" hilight semibold>
153
160
  {t('cancel')}
154
161
  </Text>
155
162
  </TouchableOpacity>
156
- <TouchableOpacity onPress={onPressSave}>
163
+ <TouchableOpacity
164
+ onPress={onPressSave}
165
+ accessibilityLabel={AccessibilityLabel.BUTTON_SAVE_EDIT_ACTION_LIST}
166
+ >
157
167
  <Text type="H4" hilight semibold>
158
168
  {t('save')}
159
169
  </Text>
@@ -189,6 +199,6 @@ const EditActionsList = () => {
189
199
  </ModalBottom>
190
200
  </View>
191
201
  );
192
- };
202
+ });
193
203
 
194
204
  export default EditActionsList;
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+
4
+ import { useTranslations } from '../../../../hooks/Common/useTranslations';
5
+ import Text from '../../../../commons/Text';
6
+ import { ViewButtonBottom } from '../../../../commons';
7
+ import BottomSheet from '../../../../commons/BottomSheet';
8
+
9
+ const InvalidQRCode = ({ isInvalidQrCode, goBack, onRetry }) => {
10
+ const t = useTranslations();
11
+
12
+ return (
13
+ <BottomSheet isVisible={isInvalidQrCode} title={t('invalid_qr_code')}>
14
+ <Text style={styles.warningContainer}>
15
+ {t('invalid_qr_code_warning')}
16
+ </Text>
17
+ <ViewButtonBottom
18
+ leftTitle={t('cancel')}
19
+ onLeftClick={goBack}
20
+ rightTitle={t('retry')}
21
+ onRightClick={onRetry}
22
+ />
23
+ </BottomSheet>
24
+ );
25
+ };
26
+
27
+ export default InvalidQRCode;
28
+
29
+ const styles = StyleSheet.create({
30
+ warningContainer: {
31
+ paddingHorizontal: 16,
32
+ },
33
+ });
@@ -46,7 +46,7 @@ const VerifingQRCode = memo(() => {
46
46
  );
47
47
  });
48
48
 
49
- const QRScan = ({ onScan }) => {
49
+ const QRScan = ({ isScanReady = true, onScan }) => {
50
50
  const t = useTranslations();
51
51
  const navigation = useNavigation();
52
52
  const isFocused = useIsFocused();
@@ -55,12 +55,14 @@ const QRScan = ({ onScan }) => {
55
55
 
56
56
  const onBarCodeRead = useCallback(
57
57
  (e) => {
58
- setLoading(true);
59
- if (!loading) {
60
- onScan(e.data, setLoading);
58
+ if (isScanReady) {
59
+ setLoading(true);
60
+ if (!loading) {
61
+ onScan(e.data, setLoading);
62
+ }
61
63
  }
62
64
  },
63
- [loading, onScan]
65
+ [isScanReady, loading, onScan]
64
66
  );
65
67
 
66
68
  const onPressClose = useCallback(() => {
@@ -4,6 +4,12 @@ import SharedUnit from '../index';
4
4
  import Modal from 'react-native-modal';
5
5
  import { SCProvider } from '../../../context';
6
6
  import { mockSCStore } from '../../../context/mockStore';
7
+ import { AccessibilityLabel } from '../../../configs/Constants';
8
+ import MockAdapter from 'axios-mock-adapter';
9
+ import api from '../../../utils/Apis/axios';
10
+ import { API } from '../../../configs';
11
+
12
+ const mock = new MockAdapter(api.axiosInstance);
7
13
 
8
14
  const wrapComponent = () => (
9
15
  <SCProvider initState={mockSCStore({})}>
@@ -16,9 +22,11 @@ jest.mock('react-redux', () => ({
16
22
  useSelector: jest.fn(),
17
23
  }));
18
24
 
25
+ const mockSetSharedUnits = jest.fn();
19
26
  jest.mock('react', () => ({
20
27
  ...jest.requireActual('react'),
21
28
  useLayoutEffect: jest.fn(),
29
+ useState: jest.fn((init) => [init, mockSetSharedUnits]),
22
30
  }));
23
31
 
24
32
  jest.mock('react-native-onesignal', () => {
@@ -37,4 +45,21 @@ describe('test SharedUnit', () => {
37
45
  const modalShareds = instance.findAllByType(Modal);
38
46
  expect(modalShareds[0]).not.toBeUndefined();
39
47
  });
48
+
49
+ test('render touch item filter', async () => {
50
+ mock.onGet(API.UNIT.FILTER_SHARED_UNITS()).reply(200, [{}]);
51
+
52
+ await act(async () => {
53
+ tree = await create(wrapComponent());
54
+ });
55
+ const instance = tree.root;
56
+ const item = instance.findAll(
57
+ (el) =>
58
+ el.props.accessibilityLabel === AccessibilityLabel.FILTER_SHARING_ITEM
59
+ );
60
+ await act(async () => {
61
+ item[0].props.onPress();
62
+ });
63
+ expect(mockSetSharedUnits).toBeCalled();
64
+ });
40
65
  });
@@ -12,6 +12,7 @@ import {
12
12
  useBoolean,
13
13
  useBlockBackAndroid,
14
14
  } from '../../hooks/Common';
15
+ import { AccessibilityLabel } from '../../configs/Constants';
15
16
 
16
17
  import styles from './styles';
17
18
  import TabHeader from './TabHeader';
@@ -185,6 +186,7 @@ const Shared = () => {
185
186
  style={styles.row}
186
187
  onPress={() => onFilter(item.filter, item.textFilter)}
187
188
  key={`filter-${index}`}
189
+ accessibilityLabel={`${AccessibilityLabel.FILTER_SHARING_ITEM}`}
188
190
  >
189
191
  <Text
190
192
  style={
@@ -1,5 +1,12 @@
1
1
  import React, { memo, useEffect, useMemo, useState } from 'react';
2
- import { View, Text, LayoutAnimation, Platform, UIManager } from 'react-native';
2
+ import {
3
+ View,
4
+ Text,
5
+ LayoutAnimation,
6
+ Platform,
7
+ UIManager,
8
+ TouchableOpacity,
9
+ } from 'react-native';
3
10
  import { IconOutline } from '@ant-design/icons-react-native';
4
11
  import { Colors } from '../../../configs';
5
12
  import styles from './Styles/DeviceItemStyles';
@@ -13,6 +20,7 @@ const DeviceItem = ({
13
20
  isRenderSeparated,
14
21
  onTickedChild,
15
22
  onTickedDevice,
23
+ onTickedDeviceIcon,
16
24
  isItemExpanded,
17
25
  toggleItem,
18
26
  idGroup,
@@ -57,6 +65,14 @@ const DeviceItem = ({
57
65
  );
58
66
  };
59
67
 
68
+ const isCheckedIcon = useMemo(() => {
69
+ return dataConfig.some((i) => i.isChecked);
70
+ }, [dataConfig]);
71
+
72
+ const handleOnTickedIcon = () => {
73
+ onTickedDeviceIcon && onTickedDeviceIcon(idGroup, id, isCheckedIcon);
74
+ };
75
+
60
76
  const handleOnTickedDevice = () => {
61
77
  onTickedDevice && onTickedDevice(idGroup, id, !isChecked);
62
78
  };
@@ -88,13 +104,13 @@ const DeviceItem = ({
88
104
 
89
105
  return (
90
106
  <View style={[styles.wrap, !isRenderSeparated && styles.isRenderSeparated]}>
91
- <FImage
92
- source={{ uri: icon_kit }}
93
- style={styles.viewLeft}
94
- tintColor={
95
- dataConfig.some((i) => i.isChecked) ? Colors.Primary : Colors.Gray
96
- }
97
- />
107
+ <TouchableOpacity onPress={handleOnTickedIcon}>
108
+ <FImage
109
+ source={{ uri: icon_kit }}
110
+ style={styles.viewLeft}
111
+ tintColor={isCheckedIcon ? Colors.Primary : Colors.Gray}
112
+ />
113
+ </TouchableOpacity>
98
114
  <View style={styles.wrapRight}>
99
115
  <View style={styles.viewRight}>
100
116
  <Text
@@ -84,17 +84,6 @@ const SelectPermission = ({ route }) => {
84
84
  }
85
85
  };
86
86
 
87
- const stationCheck = (data) => {
88
- for (let station of data) {
89
- if (station.devices.length) {
90
- station.isChecked = !station.devices.some((i) => !i.isChecked);
91
- }
92
- }
93
- setIsTickAllDevices(!dataStationTemp.some((i) => !i.isChecked));
94
- dataStationTemp = data;
95
- setDataStations(data);
96
- };
97
-
98
87
  const onTickedChild = (
99
88
  idGroup,
100
89
  deviceId,
@@ -114,8 +103,18 @@ const SelectPermission = ({ route }) => {
114
103
  device.read_configs.some((i) => !i.isChecked) ||
115
104
  device.actions.some((i) => !i.isChecked)
116
105
  );
117
-
118
- stationCheck(data);
106
+ for (let station of data) {
107
+ if (station.devices.length) {
108
+ station.isChecked = station.devices.some(
109
+ (i) =>
110
+ i.read_configs.some((r) => r.isChecked) ||
111
+ i.actions.some((a) => a.isChecked)
112
+ );
113
+ }
114
+ }
115
+ setIsTickAllDevices(!dataStationTemp.some((i) => !i.isChecked));
116
+ dataStationTemp = data;
117
+ setDataStations(data);
119
118
  };
120
119
 
121
120
  const onTickedDevice = (idGroup, deviceId, isChecked) => {
@@ -125,7 +124,48 @@ const SelectPermission = ({ route }) => {
125
124
 
126
125
  device.isChecked = isChecked;
127
126
 
128
- stationCheck(data);
127
+ for (let station of data) {
128
+ if (station.devices.length) {
129
+ station.isChecked = !station.devices.some((i) => !i.isChecked);
130
+ }
131
+ }
132
+ setIsTickAllDevices(!dataStationTemp.some((i) => !i.isChecked));
133
+ dataStationTemp = data;
134
+ setDataStations(data);
135
+ };
136
+
137
+ const onTickedDeviceIcon = (idGroup, deviceId, isChecked) => {
138
+ let data = [...dataStationTemp];
139
+ const group = data.find((i) => i.id === idGroup);
140
+ const device = group.devices.find((i) => i.id === deviceId);
141
+ const index = data.findIndex((item) => item.id === idGroup);
142
+
143
+ data[index] = {
144
+ ...data[index],
145
+ devices: data[index]?.devices.map((i) => {
146
+ if (i.id === device?.id) {
147
+ return {
148
+ ...i,
149
+ isChecked: !isChecked,
150
+ actions: i.actions.map((j) => ({ ...j, isChecked: !isChecked })),
151
+ read_configs: i.read_configs.map((j) => ({
152
+ ...j,
153
+ isChecked: !isChecked,
154
+ })),
155
+ };
156
+ }
157
+ return { ...i };
158
+ }),
159
+ };
160
+
161
+ data[index] = {
162
+ ...data[index],
163
+ isChecked:
164
+ !data[index]?.devices.length ===
165
+ !data[index]?.devices.filter((i) => i.isChecked).length,
166
+ };
167
+
168
+ setDataStations(data);
129
169
  };
130
170
 
131
171
  const toggleDeviceItem = (deviceItem) => () => {
@@ -142,15 +182,8 @@ const SelectPermission = ({ route }) => {
142
182
  const autoCheckedGroup = useCallback(async () => {
143
183
  if (isUpdateSharedDevice) {
144
184
  if (hasDataChecked) {
145
- const stationIds = object_Ids(dataDeviceShared)?.stationIds;
146
- const deviceIds = object_Ids(dataDeviceShared)?.deviceIds;
147
- const actionIds = object_Ids(dataDeviceShared)?.actionIds;
148
- const configIds = object_Ids(dataDeviceShared)?.configIds;
149
- const device_checked = [];
150
- const action_checked = [];
151
- const config_checked = [];
152
- const action_temp = [];
153
- const config_temp = [];
185
+ const { stationIds, deviceIds, actionIds, configIds } =
186
+ object_Ids(dataDeviceShared);
154
187
  for (let station in dataStations) {
155
188
  for (let item in dataStations[station].devices) {
156
189
  const itemTemp = dataStations[station].devices[item];
@@ -158,54 +191,34 @@ const SelectPermission = ({ route }) => {
158
191
  ...itemTemp,
159
192
  isChecked: deviceIds.includes(itemTemp?.id),
160
193
  actions: itemTemp.actions.map((i) => {
161
- if (actionIds?.includes(i.id)) {
162
- action_checked.push(i);
163
- }
164
- action_temp.push(i);
165
194
  return {
166
195
  ...i,
167
196
  isChecked: actionIds?.includes(i.id),
168
197
  };
169
198
  }),
170
199
  read_configs: itemTemp.read_configs.map((i) => {
171
- if (configIds?.includes(i.id)) {
172
- config_checked.push(i);
173
- }
174
- config_temp.push(i);
175
200
  return {
176
201
  ...i,
177
202
  isChecked: configIds?.includes(i.id),
178
203
  };
179
204
  }),
180
205
  };
181
- if (dataStations[station].devices[item]?.isChecked) {
182
- device_checked.push(dataStations[station].devices[item]);
183
- }
184
206
  }
185
207
  }
186
- const check_action_config = () => {
187
- return (
188
- action_checked?.length === action_temp?.length &&
189
- config_checked?.length === config_temp?.length
190
- );
191
- };
192
208
  const data = dataStations?.map((i) => {
209
+ const devices = i?.devices;
210
+ const devices_checked = i?.devices?.filter(
211
+ (device) => device?.isChecked
212
+ );
193
213
  return {
194
214
  ...i,
195
215
  isChecked:
196
- check_action_config() &&
197
- i?.devices?.length === device_checked?.length &&
216
+ !devices.length === !devices_checked.length &&
198
217
  stationIds.includes(i.id),
199
218
  };
200
219
  });
201
- const deviceIdsDefault = object_Ids(data)?.deviceIds;
202
- const stationIdsDefault = object_Ids(data)?.stationIds;
203
220
  setDataStations(data);
204
- setIsTickAllDevices(
205
- check_action_config() &&
206
- deviceIdsDefault?.length === deviceIds?.length &&
207
- stationIdsDefault?.length === stationIds?.length
208
- );
221
+ setIsTickAllDevices(data?.length === stationIds?.length);
209
222
  setHasDataChecked(false);
210
223
  }
211
224
  }
@@ -231,6 +244,7 @@ const SelectPermission = ({ route }) => {
231
244
  isRenderSeparated={index !== devices?.length - 1}
232
245
  onTickedChild={onTickedChild}
233
246
  onTickedDevice={onTickedDevice}
247
+ onTickedDeviceIcon={onTickedDeviceIcon}
234
248
  isItemExpanded={expandedItemIds.includes(i.id)}
235
249
  toggleItem={toggleDeviceItem(i)}
236
250
  idGroup={id}
@@ -331,33 +345,35 @@ const SelectPermission = ({ route }) => {
331
345
 
332
346
  const getShareUnitPermission = useCallback(async () => {
333
347
  if (isUpdateSharedDevice && dataStations?.length > 0) {
334
- const response = await axiosGet(
348
+ const { success, data } = await axiosGet(
335
349
  API.SHARE.UNIT_MEMBER_SHARE_DEVICE(unit?.id, member?.id)
336
350
  );
337
- if (response?.success) {
338
- setDataDeviceShared(response?.data);
351
+ if (success) {
352
+ setDataDeviceShared(data);
339
353
  setHasDataChecked(true);
340
354
  }
341
355
  }
342
356
  }, [dataStations?.length, isUpdateSharedDevice, member?.id, unit?.id]);
343
357
 
344
- useEffect(() => {
345
- getShareUnitPermission();
346
- }, [getShareUnitPermission]);
358
+ const getUnitPermission = useCallback(async () => {
359
+ const { success, data } = await axiosGet(
360
+ API.SHARE.UNIT_PERMISSIONS(unit?.id)
361
+ );
362
+ if (success) {
363
+ setDataStations(data);
364
+ }
365
+ setLoading(false);
366
+ }, [unit?.id]);
347
367
 
348
368
  useEffect(() => {
349
369
  (async () => {
350
370
  if (!unit) {
351
371
  return;
352
372
  }
353
-
354
- const { success, data } = await axiosGet(
355
- API.SHARE.UNIT_PERMISSIONS(unit.id)
356
- );
357
- success && setDataStations(data);
358
- setLoading(false);
373
+ await getUnitPermission();
374
+ await getShareUnitPermission();
359
375
  })();
360
- }, [dataStations?.length, isUpdateSharedDevice, member?.id, unit]);
376
+ }, [getShareUnitPermission, getUnitPermission, unit]);
361
377
 
362
378
  useEffect(() => {
363
379
  dataStationTemp = dataStations;
@@ -43,7 +43,7 @@ const wrapComponent = (route) => (
43
43
 
44
44
  describe('Test SelectPermission', () => {
45
45
  let tree;
46
- let route = { params: { unit: null } };
46
+ let route = { params: { unit: { id: 1, name: 'unit 1' } } };
47
47
  let listDevices = [
48
48
  {
49
49
  id: 204,
@@ -110,7 +110,31 @@ describe('Test SelectPermission', () => {
110
110
  expect(TextElement[2].props.children).toBe('No data');
111
111
  });
112
112
 
113
- it('test get unit fail', () => {
113
+ it('test unit get permission default', () => {
114
+ mocSetdata();
115
+ const routes = { params: { unit: { id: 1, name: '123' } } };
116
+ mock.onGet(API.SHARE.UNIT_PERMISSIONS(1)).reply(200);
117
+ act(() => {
118
+ tree = create(wrapComponent(routes));
119
+ });
120
+ const instance = tree.root;
121
+ const FlatListElement = instance.findAllByType(FlatList);
122
+ expect(FlatListElement).toHaveLength(1);
123
+ });
124
+
125
+ it('test unit fail default', () => {
126
+ mockLoading();
127
+ const routes = { params: { unit: null } };
128
+ mock.onGet(API.SHARE.UNIT_PERMISSIONS(1)).reply(400);
129
+ act(() => {
130
+ tree = create(wrapComponent(routes));
131
+ });
132
+ const instance = tree.root;
133
+ const TextElement = instance.findAllByType(Text);
134
+ expect(TextElement[2].props.children).toBe('No data');
135
+ });
136
+
137
+ it('test get unit fail shared', () => {
114
138
  mockLoading();
115
139
  route.params.unit = 1;
116
140
  mock.onGet(API.SHARE.UNIT_MEMBER_SHARE_DEVICE(1, 1)).reply(400);
@@ -172,6 +196,35 @@ describe('Test SelectPermission', () => {
172
196
  expect(mockSetDataStations).toBeCalled();
173
197
  });
174
198
 
199
+ it('test onTickedDevice function', () => {
200
+ mocSetdata();
201
+ act(() => {
202
+ tree = create(wrapComponent(route));
203
+ });
204
+ const instance = tree.root;
205
+ const DeviceItemElement = instance.findAllByType(DeviceItem);
206
+ expect(DeviceItemElement).toHaveLength(1);
207
+ act(() => {
208
+ DeviceItemElement[0].props.onTickedDevice(204, 123, true);
209
+ DeviceItemElement[0].props.toggleItem(123);
210
+ });
211
+ expect(mockSetDataStations).toBeCalled();
212
+ });
213
+
214
+ it('test onTickedDeviceIcon function', () => {
215
+ mocSetdata();
216
+ act(() => {
217
+ tree = create(wrapComponent(route));
218
+ });
219
+ const instance = tree.root;
220
+ const DeviceItemElement = instance.findAllByType(DeviceItem);
221
+ expect(DeviceItemElement).toHaveLength(1);
222
+ act(() => {
223
+ DeviceItemElement[0].props.onTickedDeviceIcon(204, 123, true);
224
+ });
225
+ expect(mockSetDataStations).toBeCalled();
226
+ });
227
+
175
228
  it('test ViewButtonBottom', () => {
176
229
  mocSetdata();
177
230
  act(() => {