@eohjsc/react-native-smart-city 0.3.93 → 0.3.94
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.
- package/package.json +1 -1
- package/src/commons/SubUnit/OneTap/ItemOneTap.js +83 -85
- package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +68 -24
- package/src/commons/SubUnit/OneTap/index.js +21 -1
- package/src/configs/API.js +1 -0
- package/src/screens/AddNewGateway/ConnectingDevice.js +7 -0
- package/src/screens/AddNewGateway/RenameNewDevices.js +2 -2
- package/src/screens/AddNewGateway/SelectDeviceType.js +67 -9
- package/src/screens/AddNewGateway/__test__/ConnectingZigbeeDevice.test.js +35 -0
- package/src/screens/AddNewGateway/__test__/SelectDeviceType.test.js +183 -29
- package/src/screens/Automate/MultiUnits.js +28 -18
- package/src/screens/Automate/ScriptDetail/__test__/index.test.js +36 -2
- package/src/screens/Automate/ScriptDetail/index.js +17 -3
- package/src/screens/Automate/Styles/MultiUnitsStyles.js +1 -1
- package/src/screens/Automate/__test__/MultiUnits.test.js +63 -6
- package/src/screens/Automate/__test__/index.test.js +44 -10
- package/src/screens/Automate/index.js +53 -38
- package/src/screens/Device/hooks/__test__/useEvaluateValue.test.js +24 -1
- package/src/screens/Sharing/InfoMemberUnit.js +2 -2
- package/src/screens/Sharing/MemberList.js +28 -7
- package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +32 -18
- package/src/screens/Sharing/__test__/MemberList.test.js +37 -4
- package/src/screens/Unit/AddMenu.js +42 -19
- package/src/screens/Unit/__test__/AddMenu.test.js +68 -15
- package/src/screens/Unit/components/AutomateScript/index.js +1 -1
- package/src/utils/I18n/translations/en.js +1409 -0
- package/src/utils/I18n/translations/vi.js +1411 -0
- package/src/utils/I18n/translations.ts +2 -2
- package/src/utils/Permission/backend.js +7 -0
- package/src/screens/Sharing/__test__/MemberList2.test.js +0 -74
- package/src/utils/I18n/translations/en.json +0 -1142
- package/src/utils/I18n/translations/vi.json +0 -1139
package/package.json
CHANGED
|
@@ -19,99 +19,97 @@ import { useNavigation } from '@react-navigation/native';
|
|
|
19
19
|
import Routes from '../../../utils/Route';
|
|
20
20
|
import { AccessibilityLabel, AUTOMATE_TYPE } from '../../../configs/Constants';
|
|
21
21
|
|
|
22
|
-
const ItemOneTap = memo(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const t = useTranslations();
|
|
22
|
+
const ItemOneTap = memo(({ automate = {}, wrapSyles, onPressItem }) => {
|
|
23
|
+
const { navigate } = useNavigation();
|
|
24
|
+
const { id, type, script, activate_at, author = '' } = automate;
|
|
25
|
+
const t = useTranslations();
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
const goToDetail = useCallback(() => {
|
|
28
|
+
navigate(Routes.ScriptDetail, {
|
|
29
|
+
id,
|
|
30
|
+
preAutomate: automate,
|
|
31
|
+
});
|
|
32
|
+
}, [automate, navigate, id]);
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
const handleScriptAction = useCallback(async () => {
|
|
35
|
+
const { success } = await axiosPost(API.AUTOMATE.ACTION_ONE_TAP(id));
|
|
36
|
+
if (success) {
|
|
37
|
+
ToastBottomHelper.success(t('activated_successfully'));
|
|
38
|
+
} else {
|
|
39
|
+
ToastBottomHelper.error(t('activation_failed'));
|
|
40
|
+
}
|
|
41
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
42
|
+
}, [id]);
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
44
|
+
const displayIcon = () => {
|
|
45
|
+
const iconKit = script?.icon_kit;
|
|
46
|
+
if (iconKit) {
|
|
47
|
+
return <FImage source={{ uri: iconKit }} style={styles.iconSensor} />;
|
|
48
|
+
}
|
|
49
|
+
if (type === AUTOMATE_TYPE.ONE_TAP) {
|
|
50
|
+
return <OneTap />;
|
|
51
|
+
}
|
|
52
|
+
if (type === AUTOMATE_TYPE.VALUE_CHANGE) {
|
|
53
|
+
return <ValueChange />;
|
|
54
|
+
}
|
|
55
|
+
if ([AUTOMATE_TYPE.EVENT].includes(type)) {
|
|
56
|
+
return <Event />;
|
|
57
|
+
}
|
|
58
|
+
return <Schedule />;
|
|
59
|
+
};
|
|
60
|
+
const activateAt = activate_at
|
|
61
|
+
? timeDifference(new Date(), moment(activate_at), true)
|
|
62
|
+
: null;
|
|
63
|
+
return (
|
|
64
|
+
<TouchableWithoutFeedback
|
|
65
|
+
onPress={onPressItem || goToDetail}
|
|
66
|
+
accessibilityLabel={`${AccessibilityLabel.AUTOMATE_SCRIPT_NAME}-${id}`}
|
|
67
|
+
>
|
|
68
|
+
<View style={[styles.container, wrapSyles]}>
|
|
69
|
+
<View style={styles.boxIcon}>
|
|
70
|
+
{displayIcon()}
|
|
71
|
+
{type === AUTOMATE_TYPE.ONE_TAP && (
|
|
72
|
+
<TouchableOpacity
|
|
73
|
+
accessibilityLabel={AccessibilityLabel.AUTOMATE_SCRIPT_ACTION}
|
|
74
|
+
onPress={handleScriptAction}
|
|
75
|
+
>
|
|
76
|
+
<CheckCircle />
|
|
77
|
+
</TouchableOpacity>
|
|
78
|
+
)}
|
|
79
|
+
</View>
|
|
80
|
+
<TouchableOpacity
|
|
81
|
+
accessibilityLabel={AccessibilityLabel.GO_DETAIL}
|
|
82
|
+
onPress={onPressItem || goToDetail}
|
|
83
|
+
>
|
|
84
|
+
<Text
|
|
85
|
+
numberOfLines={1}
|
|
86
|
+
semibold
|
|
87
|
+
size={14}
|
|
88
|
+
color={Colors.Gray9}
|
|
89
|
+
type="Body"
|
|
90
|
+
style={styles.name}
|
|
84
91
|
>
|
|
92
|
+
{script?.name}
|
|
93
|
+
</Text>
|
|
94
|
+
<Text numberOfLines={1} type={'Label'} color={Colors.Gray7}>
|
|
95
|
+
{t('created_by', { name: author })}
|
|
96
|
+
</Text>
|
|
97
|
+
<View style={styles.descriptionContainer}>
|
|
85
98
|
<Text
|
|
86
99
|
numberOfLines={1}
|
|
87
100
|
semibold
|
|
88
|
-
size={
|
|
89
|
-
color={Colors.
|
|
90
|
-
type="
|
|
91
|
-
style={styles.name}
|
|
101
|
+
size={12}
|
|
102
|
+
color={Colors.Gray8}
|
|
103
|
+
type="Label"
|
|
92
104
|
>
|
|
93
|
-
{
|
|
105
|
+
{activateAt && t('activated_time', { time: activateAt })}
|
|
94
106
|
</Text>
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
size={12}
|
|
103
|
-
color={Colors.Gray8}
|
|
104
|
-
type="Label"
|
|
105
|
-
>
|
|
106
|
-
{activateAt && t('activated_time', { time: activateAt })}
|
|
107
|
-
</Text>
|
|
108
|
-
<IconOutline name="right" size={12} />
|
|
109
|
-
</View>
|
|
110
|
-
</TouchableOpacity>
|
|
111
|
-
</View>
|
|
112
|
-
</TouchableWithoutFeedback>
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
);
|
|
107
|
+
<IconOutline name="right" size={12} />
|
|
108
|
+
</View>
|
|
109
|
+
</TouchableOpacity>
|
|
110
|
+
</View>
|
|
111
|
+
</TouchableWithoutFeedback>
|
|
112
|
+
);
|
|
113
|
+
});
|
|
116
114
|
|
|
117
115
|
export default ItemOneTap;
|
|
@@ -16,11 +16,13 @@ import Routes from '../../../../utils/Route';
|
|
|
16
16
|
import api from '../../../../utils/Apis/axios';
|
|
17
17
|
import { API } from '../../../../configs';
|
|
18
18
|
import { useNavigation } from '@react-navigation/native';
|
|
19
|
+
import { ToastBottomHelper } from '../../../../utils/Utils';
|
|
20
|
+
import { getTranslate } from '../../../../utils/I18n';
|
|
19
21
|
|
|
20
22
|
const mock = new MockAdapter(api.axiosInstance);
|
|
21
23
|
|
|
22
|
-
const wrapComponent = (data) => (
|
|
23
|
-
<SCProvider initState={mockSCStore(
|
|
24
|
+
const wrapComponent = (data, storeData = {}) => (
|
|
25
|
+
<SCProvider initState={mockSCStore(storeData)}>
|
|
24
26
|
<SubUnitAutomate {...data} />
|
|
25
27
|
</SCProvider>
|
|
26
28
|
);
|
|
@@ -33,33 +35,34 @@ jest.mock('react-redux', () => {
|
|
|
33
35
|
});
|
|
34
36
|
|
|
35
37
|
let tree;
|
|
36
|
-
let data
|
|
37
|
-
isOwner: true,
|
|
38
|
-
listAutomate: [
|
|
39
|
-
{
|
|
40
|
-
text: 'Scenario',
|
|
41
|
-
data: [
|
|
42
|
-
{
|
|
43
|
-
id: 1,
|
|
44
|
-
user: 6,
|
|
45
|
-
type: 'one_tap',
|
|
46
|
-
activate_at: '2021-09-17T05:30:00Z',
|
|
47
|
-
script: {
|
|
48
|
-
name: 'Joshua Ray',
|
|
49
|
-
icon: '',
|
|
50
|
-
icon_kit: '',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
type: AUTOMATE_TABS.SCENARIO,
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
};
|
|
38
|
+
let data;
|
|
58
39
|
|
|
59
40
|
describe('test Item', () => {
|
|
60
41
|
const mockedNavigate = useNavigation().navigate;
|
|
61
42
|
beforeEach(() => {
|
|
62
43
|
mockedNavigate.mockClear();
|
|
44
|
+
data = {
|
|
45
|
+
isOwner: true,
|
|
46
|
+
listAutomate: [
|
|
47
|
+
{
|
|
48
|
+
text: 'Scenario',
|
|
49
|
+
data: [
|
|
50
|
+
{
|
|
51
|
+
id: 1,
|
|
52
|
+
user: 6,
|
|
53
|
+
type: 'one_tap',
|
|
54
|
+
activate_at: '2021-09-17T05:30:00Z',
|
|
55
|
+
script: {
|
|
56
|
+
name: 'Joshua Ray',
|
|
57
|
+
icon: '',
|
|
58
|
+
icon_kit: '',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
type: AUTOMATE_TABS.SCENARIO,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
63
66
|
});
|
|
64
67
|
|
|
65
68
|
it('render SubUnitAutomate isOwner and handleOnAddNew', async () => {
|
|
@@ -189,6 +192,7 @@ describe('test Item', () => {
|
|
|
189
192
|
preAutomate: data.listAutomate[0].data[0],
|
|
190
193
|
});
|
|
191
194
|
});
|
|
195
|
+
|
|
192
196
|
it('render SubUnitAutomate script schedule and handleOnAddNew item automate', async () => {
|
|
193
197
|
data.isOwner = false;
|
|
194
198
|
data.type = 'schedule';
|
|
@@ -242,6 +246,46 @@ describe('test Item', () => {
|
|
|
242
246
|
expect(goDetail).toHaveLength(1);
|
|
243
247
|
});
|
|
244
248
|
|
|
249
|
+
it('add new automate when reach maximum', async () => {
|
|
250
|
+
data.isOwner = false;
|
|
251
|
+
data.type = 'schedule';
|
|
252
|
+
data.unit = { id: 1 };
|
|
253
|
+
|
|
254
|
+
await act(async () => {
|
|
255
|
+
tree = await create(
|
|
256
|
+
wrapComponent(data, {
|
|
257
|
+
auth: {
|
|
258
|
+
account: {
|
|
259
|
+
user: {
|
|
260
|
+
permissions: {
|
|
261
|
+
max_automations_per_unit: 0,
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const instance = tree.root;
|
|
271
|
+
const item = instance.findAll(
|
|
272
|
+
(el) =>
|
|
273
|
+
el.props.accessibilityLabel ===
|
|
274
|
+
AccessibilityLabel.SUB_UNIT_ADD_DEVICE &&
|
|
275
|
+
el.type === TouchableWithoutFeedback
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
expect(item).toHaveLength(1);
|
|
279
|
+
const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
|
|
280
|
+
await act(async () => {
|
|
281
|
+
await item[0].props.onPress();
|
|
282
|
+
});
|
|
283
|
+
expect(mockedNavigate).not.toBeCalled();
|
|
284
|
+
expect(spyToastError).toBeCalledWith(
|
|
285
|
+
getTranslate('en', 'reach_max_automations_per_unit')
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
|
|
245
289
|
it('render click select option by automation', async () => {
|
|
246
290
|
await act(async () => {
|
|
247
291
|
tree = await create(wrapComponent(data));
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
2
8
|
import { TouchableOpacity, View } from 'react-native';
|
|
3
9
|
|
|
4
10
|
import { Section } from '../..';
|
|
@@ -15,15 +21,29 @@ import {
|
|
|
15
21
|
import Text from '../../Text/index.js';
|
|
16
22
|
|
|
17
23
|
import styles from './OneTapStyles.js';
|
|
24
|
+
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
25
|
+
import { useBackendPermission } from '../../../utils/Permission/backend';
|
|
18
26
|
|
|
19
27
|
const SubUnitAutomate = ({ isOwner, listAutomate, unit, wrapItemStyle }) => {
|
|
20
28
|
const t = useTranslations();
|
|
21
29
|
const { navigate } = useNavigation();
|
|
22
30
|
const [automates, setAutomates] = useState(listAutomate[0]);
|
|
23
31
|
const [indexAutomate, setIndexAutomate] = useState(0);
|
|
32
|
+
const permissions = useBackendPermission();
|
|
24
33
|
const { name: currentScreen } = useRoute();
|
|
25
34
|
|
|
35
|
+
const totalAutomates = useMemo(
|
|
36
|
+
() => listAutomate.reduce((acc, item) => item.data.length + acc, 0),
|
|
37
|
+
[listAutomate]
|
|
38
|
+
);
|
|
39
|
+
|
|
26
40
|
const handleOnAddNew = () => {
|
|
41
|
+
if (unit?.id) {
|
|
42
|
+
if (permissions.max_automations_per_unit <= totalAutomates) {
|
|
43
|
+
ToastBottomHelper.error(t('reach_max_automations_per_unit'));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
27
47
|
switch (automates.type) {
|
|
28
48
|
case AUTOMATE_TABS.SCENARIO:
|
|
29
49
|
navigate(Routes.ScenarioName, {
|
package/src/configs/API.js
CHANGED
|
@@ -215,6 +215,7 @@ const API = {
|
|
|
215
215
|
},
|
|
216
216
|
GATEWAY: {
|
|
217
217
|
LIST: () => '/chip_manager/developer_mode_chips/',
|
|
218
|
+
COUNT: () => '/chip_manager/developer_mode_chips/count/',
|
|
218
219
|
DETAIL: (id) => `/chip_manager/developer_mode_chips/${id}/`,
|
|
219
220
|
REBOOT: (id) => `/chip_manager/developer_mode_chips/${id}/reboot_chip/`,
|
|
220
221
|
},
|
|
@@ -21,7 +21,7 @@ const RenameNewDevices = memo(({ route }) => {
|
|
|
21
21
|
const t = useTranslations();
|
|
22
22
|
const { dispatch } = useNavigation();
|
|
23
23
|
|
|
24
|
-
const { unit, subUnit, chipId, sensorId, addDeviceType } =
|
|
24
|
+
const { unit, subUnit, chipId, sensorId, addDeviceType, isRejoin } =
|
|
25
25
|
route?.params || {};
|
|
26
26
|
const [selectedItem, setSelectedItem] = useState({});
|
|
27
27
|
const [isCanRename, setIsCanRename] = useState(false);
|
|
@@ -268,7 +268,7 @@ const RenameNewDevices = memo(({ route }) => {
|
|
|
268
268
|
size={20}
|
|
269
269
|
style={styles.textStatus}
|
|
270
270
|
>
|
|
271
|
-
{t('
|
|
271
|
+
{t(`successfully_${isRejoin ? 'rejoined' : 'connected'}`)}
|
|
272
272
|
</Text>
|
|
273
273
|
<Text size={14} color={Colors.Gray9}>
|
|
274
274
|
{[unit?.name, subUnit?.name].filter(Boolean).join(' - ')}
|
|
@@ -14,13 +14,19 @@ import AddZigbeeDeviceIcon from '../../../assets/images/AddNewDevice/add-zigbee-
|
|
|
14
14
|
import styles from './SelectDeviceTypeStyles';
|
|
15
15
|
import AccessibilityLabel from '../../configs/AccessibilityLabel';
|
|
16
16
|
import { DEVICE_TYPE } from '../../configs/Constants';
|
|
17
|
+
import { useBackendPermission } from '../../utils/Permission/backend';
|
|
18
|
+
import { axiosGet } from '../../utils/Apis/axios';
|
|
19
|
+
import API from '../../configs/API';
|
|
20
|
+
import { ToastBottomHelper } from '../../utils/Utils';
|
|
17
21
|
|
|
18
22
|
const SelectDeviceGrid = ({ options, onSelect }) => {
|
|
19
23
|
const [selectIndexes, setSelectIndexes] = useState();
|
|
20
24
|
const onSelectOption = useCallback(
|
|
21
25
|
(value) => () => {
|
|
26
|
+
if (onSelect(value) === false) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
22
29
|
setSelectIndexes(value);
|
|
23
|
-
onSelect(value);
|
|
24
30
|
},
|
|
25
31
|
[onSelect]
|
|
26
32
|
);
|
|
@@ -51,17 +57,33 @@ const SelectDeviceGrid = ({ options, onSelect }) => {
|
|
|
51
57
|
);
|
|
52
58
|
};
|
|
53
59
|
|
|
60
|
+
const getPermissionCode = (deviceType) => {
|
|
61
|
+
switch (deviceType) {
|
|
62
|
+
case 'gateway_qr':
|
|
63
|
+
return 'plug_and_play_gateway';
|
|
64
|
+
case 'wifi_device_qr':
|
|
65
|
+
return 'plug_and_play_wifi';
|
|
66
|
+
case 'modbus_device_qr':
|
|
67
|
+
return 'plug_and_play_modbus';
|
|
68
|
+
case 'zigbee':
|
|
69
|
+
default:
|
|
70
|
+
return 'plug_and_play_zigbee';
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
54
74
|
const SelectDeviceType = ({ route }) => {
|
|
55
75
|
const t = useTranslations();
|
|
56
76
|
const { unit, subUnit } = route?.params || {};
|
|
57
77
|
const { navigate, goBack } = useNavigation();
|
|
78
|
+
const [unitCountSummary, setUnitCountSummary] = useState();
|
|
58
79
|
const [addType, setAddType] = useState();
|
|
59
80
|
const [selectedAddType, setSelectedAddType] = useState();
|
|
81
|
+
const permissions = useBackendPermission();
|
|
60
82
|
|
|
61
83
|
const listDeviceType = useMemo(() => {
|
|
62
84
|
const list = [
|
|
63
85
|
{
|
|
64
|
-
id:
|
|
86
|
+
id: 'gateway_qr',
|
|
65
87
|
image: <AddGatewayIcon width={60} height={60} />,
|
|
66
88
|
route: Routes.ScanGatewayQR,
|
|
67
89
|
data: {
|
|
@@ -71,7 +93,7 @@ const SelectDeviceType = ({ route }) => {
|
|
|
71
93
|
subtitle: t('central_controller'),
|
|
72
94
|
},
|
|
73
95
|
{
|
|
74
|
-
id:
|
|
96
|
+
id: 'wifi_device_qr',
|
|
75
97
|
image: <AddWifiDeviceIcon width={60} height={60} />,
|
|
76
98
|
route: subUnit?.id
|
|
77
99
|
? Routes.ScanWifiDeviceQR
|
|
@@ -85,7 +107,7 @@ const SelectDeviceType = ({ route }) => {
|
|
|
85
107
|
subtitle: t('device_connect_without_gateway'),
|
|
86
108
|
},
|
|
87
109
|
{
|
|
88
|
-
id:
|
|
110
|
+
id: 'modbus_device_qr',
|
|
89
111
|
image: <AddModbusDeviceIcon width={60} height={60} />,
|
|
90
112
|
route: subUnit?.id ? Routes.ScanModbusQR : Routes.SelectDeviceSubUnit,
|
|
91
113
|
data: {
|
|
@@ -97,7 +119,7 @@ const SelectDeviceType = ({ route }) => {
|
|
|
97
119
|
subtitle: t('device_connect_directly_to_the_gateway'),
|
|
98
120
|
},
|
|
99
121
|
{
|
|
100
|
-
id:
|
|
122
|
+
id: 'zigbee',
|
|
101
123
|
image: <AddZigbeeDeviceIcon width={60} height={60} />,
|
|
102
124
|
route: subUnit?.id
|
|
103
125
|
? Routes.ConnectRouterGuide
|
|
@@ -111,19 +133,43 @@ const SelectDeviceType = ({ route }) => {
|
|
|
111
133
|
subtitle: t('device_connect_remotely_to_the_gateway'),
|
|
112
134
|
},
|
|
113
135
|
];
|
|
114
|
-
if (
|
|
136
|
+
if (subUnit?.id) {
|
|
115
137
|
list.shift();
|
|
116
138
|
}
|
|
117
139
|
return list;
|
|
118
140
|
}, [unit, t, subUnit]);
|
|
119
141
|
|
|
120
142
|
const onRight = useCallback(() => {
|
|
121
|
-
if (selectedAddType?.route
|
|
122
|
-
|
|
143
|
+
if (!selectedAddType?.route) {
|
|
144
|
+
return;
|
|
123
145
|
}
|
|
146
|
+
|
|
147
|
+
navigate(selectedAddType?.route, selectedAddType?.data);
|
|
124
148
|
}, [navigate, selectedAddType?.data, selectedAddType?.route]);
|
|
125
149
|
|
|
126
150
|
const handleOnSelect = (itemSelect) => {
|
|
151
|
+
const needPermission = getPermissionCode(itemSelect);
|
|
152
|
+
if (!permissions?.[needPermission]) {
|
|
153
|
+
ToastBottomHelper.error(t(`no_permission_${needPermission}`));
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (['gateway_qr', 'wifi_device_qr'].includes(itemSelect)) {
|
|
158
|
+
if (permissions?.max_chips_per_unit <= unitCountSummary?.total_chips) {
|
|
159
|
+
ToastBottomHelper.error(t('reach_max_chips_per_unit'));
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (['wifi_device_qr', 'modbus_device_qr', 'zigbee'].includes(itemSelect)) {
|
|
165
|
+
if (
|
|
166
|
+
permissions?.max_configs_per_unit <= unitCountSummary?.total_configs
|
|
167
|
+
) {
|
|
168
|
+
ToastBottomHelper.error(t('reach_max_configs_per_unit'));
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
127
173
|
setAddType(itemSelect);
|
|
128
174
|
};
|
|
129
175
|
|
|
@@ -131,6 +177,18 @@ const SelectDeviceType = ({ route }) => {
|
|
|
131
177
|
setSelectedAddType(listDeviceType.find((item) => item.id === addType));
|
|
132
178
|
}, [addType, listDeviceType]);
|
|
133
179
|
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
const fetchCountSummary = async () => {
|
|
182
|
+
const { success, data } = await axiosGet(API.DEV_MODE.GATEWAY.COUNT(), {
|
|
183
|
+
unit: unit?.id,
|
|
184
|
+
});
|
|
185
|
+
if (success) {
|
|
186
|
+
setUnitCountSummary(data);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
fetchCountSummary();
|
|
190
|
+
}, [unit?.id]);
|
|
191
|
+
|
|
134
192
|
return (
|
|
135
193
|
<View style={styles.container}>
|
|
136
194
|
<HeaderCustom title={t('choose_type_of_device')} isShowSeparator />
|
|
@@ -141,7 +199,7 @@ const SelectDeviceType = ({ route }) => {
|
|
|
141
199
|
leftTitle={t('cancel')}
|
|
142
200
|
onLeftClick={goBack}
|
|
143
201
|
rightTitle={t('text_next')}
|
|
144
|
-
rightDisabled={!addType}
|
|
202
|
+
rightDisabled={!unitCountSummary || !addType}
|
|
145
203
|
onRightClick={onRight}
|
|
146
204
|
/>
|
|
147
205
|
</View>
|
|
@@ -121,4 +121,39 @@ describe('Test connecting modbus device', () => {
|
|
|
121
121
|
sensorId: 1,
|
|
122
122
|
});
|
|
123
123
|
});
|
|
124
|
+
|
|
125
|
+
it('receive sensor from channel after rejoin zigbee', async () => {
|
|
126
|
+
mock.onPost().reply(200, {});
|
|
127
|
+
await act(async () => {
|
|
128
|
+
tree = await create(wrapComponent(route));
|
|
129
|
+
});
|
|
130
|
+
const channels = getPusher().channels;
|
|
131
|
+
let channel;
|
|
132
|
+
for (const key in channels) {
|
|
133
|
+
if (key.indexOf('cache-') === 0) {
|
|
134
|
+
channel = channels[key];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
await act(async () => {
|
|
138
|
+
await channel.trigger('progress', {
|
|
139
|
+
data: { sensor: { id: 1, name: 'sensor' }, type: 'rejoin' },
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
await act(async () => {
|
|
143
|
+
await channel.trigger('progress', { success: 1 });
|
|
144
|
+
});
|
|
145
|
+
expect(mockedNavigate).toHaveBeenCalledWith(Routes.RenameNewDevices, {
|
|
146
|
+
addDeviceType: undefined,
|
|
147
|
+
unit: {
|
|
148
|
+
id: 1,
|
|
149
|
+
},
|
|
150
|
+
subUnit: {
|
|
151
|
+
id: 2,
|
|
152
|
+
station: 'Station 2',
|
|
153
|
+
},
|
|
154
|
+
chipId: 3,
|
|
155
|
+
sensorId: 1,
|
|
156
|
+
isRejoin: true,
|
|
157
|
+
});
|
|
158
|
+
});
|
|
124
159
|
});
|