@eohjsc/react-native-smart-city 0.3.39 → 0.3.40
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/StatusBox/index.js +19 -0
- package/src/commons/StatusBox/styles.js +30 -0
- package/src/commons/index.js +8 -1
- package/src/configs/API.js +4 -0
- package/src/configs/AccessibilityLabel.js +2 -1
- package/src/configs/Colors.js +1 -0
- package/src/screens/Device/detail.js +14 -10
- package/src/screens/Gateway/__test__/index.test.js +32 -5
- package/src/screens/Gateway/components/GatewayItem/__test__/index.test.js +83 -0
- package/src/screens/Gateway/components/GatewayItem/index.js +27 -0
- package/src/screens/Gateway/components/GatewayItem/styles.js +62 -0
- package/src/screens/Gateway/hooks/useGateway.js +38 -0
- package/src/screens/Gateway/index.js +77 -5
- package/src/screens/Gateway/styles.js +32 -0
- package/src/utils/I18n/translations/en.json +4 -0
- package/src/utils/I18n/translations/vi.json +4 -0
- package/src/utils/Route/index.js +1 -0
package/package.json
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import Text from '../Text';
|
|
5
|
+
import styles from './styles';
|
|
6
|
+
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
7
|
+
|
|
8
|
+
const StatusBox = ({ status }) => {
|
|
9
|
+
const t = useTranslations();
|
|
10
|
+
return (
|
|
11
|
+
<View style={[styles.viewStatus, status && styles.viewOnline]}>
|
|
12
|
+
<Text style={[styles.textStatus, status && styles.textOnline]}>
|
|
13
|
+
{t(status ? 'online' : 'offline')}
|
|
14
|
+
</Text>
|
|
15
|
+
</View>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default memo(StatusBox);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Colors } from '../../configs';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
viewStatus: {
|
|
6
|
+
width: 65,
|
|
7
|
+
paddingHorizontal: 8,
|
|
8
|
+
paddingVertical: 4,
|
|
9
|
+
borderRadius: 54,
|
|
10
|
+
backgroundColor: Colors.White,
|
|
11
|
+
borderColor: Colors.Neutral.Neutral3,
|
|
12
|
+
borderWidth: 1,
|
|
13
|
+
},
|
|
14
|
+
textStatus: {
|
|
15
|
+
fontStyle: 'normal',
|
|
16
|
+
fontWeight: 600,
|
|
17
|
+
letterSpacing: 0.04,
|
|
18
|
+
fontSize: 12,
|
|
19
|
+
lineHeight: 16,
|
|
20
|
+
textTransform: 'uppercase',
|
|
21
|
+
textAlign: 'center',
|
|
22
|
+
},
|
|
23
|
+
textOnline: {
|
|
24
|
+
fontStyle: 'bold',
|
|
25
|
+
},
|
|
26
|
+
viewOnline: {
|
|
27
|
+
backgroundColor: Colors.LightGreen,
|
|
28
|
+
borderWidth: 0,
|
|
29
|
+
},
|
|
30
|
+
});
|
package/src/commons/index.js
CHANGED
|
@@ -16,7 +16,10 @@ import RadioCircle from './RadioCircle';
|
|
|
16
16
|
import { CircleButton } from './CircleButton';
|
|
17
17
|
import HorizontalPicker from './HorizontalPicker';
|
|
18
18
|
import FullLoading from './FullLoading';
|
|
19
|
-
|
|
19
|
+
import Text from './Text';
|
|
20
|
+
import { HeaderCustom } from './Header';
|
|
21
|
+
import StatusBox from './StatusBox';
|
|
22
|
+
import WrapHeaderScrollable from './Sharing/WrapHeaderScrollable';
|
|
20
23
|
export {
|
|
21
24
|
ButtonPopup,
|
|
22
25
|
CircleView,
|
|
@@ -36,4 +39,8 @@ export {
|
|
|
36
39
|
CircleButton,
|
|
37
40
|
HorizontalPicker,
|
|
38
41
|
FullLoading,
|
|
42
|
+
Text,
|
|
43
|
+
HeaderCustom,
|
|
44
|
+
StatusBox,
|
|
45
|
+
WrapHeaderScrollable,
|
|
39
46
|
};
|
package/src/configs/API.js
CHANGED
|
@@ -211,6 +211,10 @@ const API = {
|
|
|
211
211
|
GET_TEMPLATES: '/property_manager/iot_dashboard/dev_mode/templates/',
|
|
212
212
|
GET_WIDGETS: (templateId) =>
|
|
213
213
|
`/property_manager/iot_dashboard/dev_mode/templates/${templateId}/list_widgets/`,
|
|
214
|
+
GATEWAY: {
|
|
215
|
+
LIST: () => '/chip_manager/developer_mode_chips/',
|
|
216
|
+
DETAIL: (id) => `/chip_manager/developer_mode_chips/${id}/`,
|
|
217
|
+
},
|
|
214
218
|
},
|
|
215
219
|
};
|
|
216
220
|
|
|
@@ -638,7 +638,8 @@ export default {
|
|
|
638
638
|
BUTTON_CANCEL_EDIT_ACTION_LIST: 'BUTTON_CANCEL_EDIT_ACTION_LIST',
|
|
639
639
|
BUTTON_SAVE_EDIT_ACTION_LIST: 'BUTTON_SAVE_EDIT_ACTION_LIST',
|
|
640
640
|
BUTTON_REMOVE_EDIT_ACTION_LIST: 'BUTTON_REMOVE_EDIT_ACTION_LIST',
|
|
641
|
-
|
|
641
|
+
// GATEWAY
|
|
642
|
+
LIST_GATEWAY: 'LIST_GATEWAY',
|
|
642
643
|
//Notification
|
|
643
644
|
CUSTOM_TEXT: 'CUSTOM_TEXT',
|
|
644
645
|
//ButtonWrapper
|
package/src/configs/Colors.js
CHANGED
|
@@ -220,6 +220,15 @@ const DeviceDetail = ({ route }) => {
|
|
|
220
220
|
}
|
|
221
221
|
}, [fetchSensorDetail, sensorId, sensorData]);
|
|
222
222
|
|
|
223
|
+
const fetchRemoteControlOptions = useCallback(async () => {
|
|
224
|
+
const { success, data } = await axiosGet(
|
|
225
|
+
API.DEVICE.REMOTE_CONTROL_OPTIONS(sensor?.id),
|
|
226
|
+
{},
|
|
227
|
+
true
|
|
228
|
+
);
|
|
229
|
+
success && setControlOptions(data);
|
|
230
|
+
}, [sensor]);
|
|
231
|
+
|
|
223
232
|
const fetchDataDeviceDetail = useCallback(async () => {
|
|
224
233
|
if (!token) {
|
|
225
234
|
return;
|
|
@@ -259,16 +268,11 @@ const DeviceDetail = ({ route }) => {
|
|
|
259
268
|
}
|
|
260
269
|
setLoading((preState) => ({ ...preState, displayTemplate: false }));
|
|
261
270
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (controlResult.success) {
|
|
269
|
-
setControlOptions(controlResult.data);
|
|
270
|
-
}
|
|
271
|
-
}, [token, sensor, setDeviceId, setLastEvent]);
|
|
271
|
+
success &&
|
|
272
|
+
data.items.length &&
|
|
273
|
+
data.items.some((item) => item.type === 'action') &&
|
|
274
|
+
(await fetchRemoteControlOptions());
|
|
275
|
+
}, [token, sensor, setDeviceId, fetchRemoteControlOptions]);
|
|
272
276
|
|
|
273
277
|
const {
|
|
274
278
|
deviceId: emergencyDeviceId,
|
|
@@ -1,16 +1,43 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FlatList } from 'react-native';
|
|
3
3
|
import { act, create } from 'react-test-renderer';
|
|
4
4
|
import Gateway from '..';
|
|
5
|
+
import { SCProvider } from '../../../context';
|
|
6
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
7
|
+
import { AccessibilityLabel } from '../../../configs/Constants';
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
const wrapComponent = (route) => (
|
|
10
|
+
<SCProvider initState={mockSCStore({})}>
|
|
11
|
+
<Gateway />
|
|
12
|
+
</SCProvider>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
describe('Test Gateway screen', () => {
|
|
7
16
|
let tree;
|
|
8
17
|
it('Test render', async () => {
|
|
9
18
|
await act(async () => {
|
|
10
|
-
tree = await create(
|
|
19
|
+
tree = await create(wrapComponent());
|
|
20
|
+
});
|
|
21
|
+
const instance = tree.root;
|
|
22
|
+
const view = instance.findByProps({
|
|
23
|
+
accessibilityLabel: AccessibilityLabel.LIST_GATEWAY,
|
|
24
|
+
});
|
|
25
|
+
const flatLists = instance.findAllByType(FlatList);
|
|
26
|
+
expect(view.props.children[0].props.children).toEqual('Gateway');
|
|
27
|
+
expect(flatLists).toHaveLength(1);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('Test render empty', async () => {
|
|
31
|
+
await act(async () => {
|
|
32
|
+
tree = await create(wrapComponent());
|
|
11
33
|
});
|
|
12
34
|
const instance = tree.root;
|
|
13
|
-
const
|
|
14
|
-
expect(
|
|
35
|
+
const flatLists = instance.findAllByType(FlatList);
|
|
36
|
+
expect(
|
|
37
|
+
flatLists[0].props.ListEmptyComponent.props.children.props.text1
|
|
38
|
+
).toEqual('No Gateway yet');
|
|
39
|
+
expect(
|
|
40
|
+
flatLists[0].props.ListEmptyComponent.props.children.props.text2
|
|
41
|
+
).toEqual("You don't have any gateway");
|
|
15
42
|
});
|
|
16
43
|
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { create, act } from 'react-test-renderer';
|
|
3
|
+
import { TouchableOpacity } from 'react-native';
|
|
4
|
+
import { SCProvider } from '../../../../../context';
|
|
5
|
+
import { mockSCStore } from '../../../../../context/mockStore';
|
|
6
|
+
import GatewayItem from '..';
|
|
7
|
+
|
|
8
|
+
const mockOnPressItem = jest.fn();
|
|
9
|
+
|
|
10
|
+
const wrapComponent = (item) => (
|
|
11
|
+
<SCProvider initState={mockSCStore({})}>
|
|
12
|
+
<GatewayItem item={item} onPress={mockOnPressItem} />
|
|
13
|
+
</SCProvider>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const mockNavigate = jest.fn();
|
|
17
|
+
jest.mock('@react-navigation/native', () => {
|
|
18
|
+
return {
|
|
19
|
+
...jest.requireActual('@react-navigation/native'),
|
|
20
|
+
useNavigation: () => ({
|
|
21
|
+
navigate: mockNavigate,
|
|
22
|
+
}),
|
|
23
|
+
useIsFocused: jest.fn(),
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('Test GatewayItem', () => {
|
|
28
|
+
let tree;
|
|
29
|
+
let item;
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
item = {
|
|
32
|
+
id: 1,
|
|
33
|
+
name: 'gatewayItem',
|
|
34
|
+
is_connected: false,
|
|
35
|
+
last_healthy: 1,
|
|
36
|
+
};
|
|
37
|
+
mockNavigate.mockClear();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('test render GatewayItem connect false', async () => {
|
|
41
|
+
await act(async () => {
|
|
42
|
+
tree = await create(wrapComponent(item));
|
|
43
|
+
});
|
|
44
|
+
const instance = tree.root;
|
|
45
|
+
|
|
46
|
+
const buttons = instance.findAllByType(TouchableOpacity);
|
|
47
|
+
expect(buttons).toHaveLength(1);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('test render GatewayItem connect true', async () => {
|
|
51
|
+
item = {
|
|
52
|
+
...item,
|
|
53
|
+
is_connected: true,
|
|
54
|
+
};
|
|
55
|
+
await act(async () => {
|
|
56
|
+
tree = await create(wrapComponent(item));
|
|
57
|
+
});
|
|
58
|
+
const instance = tree.root;
|
|
59
|
+
|
|
60
|
+
const buttons = instance.findAllByType(TouchableOpacity);
|
|
61
|
+
expect(buttons).toHaveLength(1);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('test render GatewayItem onPress', async () => {
|
|
65
|
+
item = {
|
|
66
|
+
id: 1,
|
|
67
|
+
name: 'gatewayItem',
|
|
68
|
+
is_connected: false,
|
|
69
|
+
last_healthy: 1,
|
|
70
|
+
};
|
|
71
|
+
await act(async () => {
|
|
72
|
+
tree = await create(wrapComponent(item));
|
|
73
|
+
});
|
|
74
|
+
const instance = tree.root;
|
|
75
|
+
|
|
76
|
+
const buttons = instance.findAllByType(TouchableOpacity);
|
|
77
|
+
expect(buttons).toHaveLength(1);
|
|
78
|
+
await act(async () => {
|
|
79
|
+
await buttons[0].props.onPress();
|
|
80
|
+
});
|
|
81
|
+
expect(mockOnPressItem).toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { TouchableOpacity, View } from 'react-native';
|
|
3
|
+
import { Colors } from '../../../../configs';
|
|
4
|
+
import { Text, StatusBox } from '../../../../commons';
|
|
5
|
+
import styles from './styles';
|
|
6
|
+
|
|
7
|
+
const GatewayItem = ({ item, onPress }) => {
|
|
8
|
+
return (
|
|
9
|
+
<View style={styles.wrap}>
|
|
10
|
+
<TouchableOpacity style={styles.button} onPress={onPress}>
|
|
11
|
+
<View style={styles.viewTitle}>
|
|
12
|
+
<Text type="Body" bold color={Colors.Gray9} style={styles.textTitle}>
|
|
13
|
+
{item?.name}
|
|
14
|
+
</Text>
|
|
15
|
+
</View>
|
|
16
|
+
<View style={styles.viewValue}>
|
|
17
|
+
<Text type="Body" color={Colors.Gray20} style={styles.textValue}>
|
|
18
|
+
{`${item?.last_healthy || '--'} dBm`}
|
|
19
|
+
</Text>
|
|
20
|
+
</View>
|
|
21
|
+
<StatusBox status={item?.is_connected} />
|
|
22
|
+
</TouchableOpacity>
|
|
23
|
+
</View>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default memo(GatewayItem);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Colors, Constants } from '../../../../configs';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
wrap: {
|
|
6
|
+
padding: 16,
|
|
7
|
+
marginBottom: 16,
|
|
8
|
+
backgroundColor: Colors.White,
|
|
9
|
+
borderRadius: 8,
|
|
10
|
+
width: Constants.width / 2 - 24,
|
|
11
|
+
borderWidth: 1,
|
|
12
|
+
borderColor: Colors.Neutral.Neutral3,
|
|
13
|
+
},
|
|
14
|
+
button: {
|
|
15
|
+
flex: 1,
|
|
16
|
+
justifyContent: 'space-between',
|
|
17
|
+
},
|
|
18
|
+
viewTitle: {
|
|
19
|
+
marginBottom: 6,
|
|
20
|
+
},
|
|
21
|
+
viewValue: {
|
|
22
|
+
marginBottom: 6,
|
|
23
|
+
},
|
|
24
|
+
textTitle: {
|
|
25
|
+
lineHeight: 20,
|
|
26
|
+
height: 40,
|
|
27
|
+
fontWeight: 600,
|
|
28
|
+
textOverflow: 'ellipsis',
|
|
29
|
+
},
|
|
30
|
+
textValue: {
|
|
31
|
+
fontWeight: 400,
|
|
32
|
+
},
|
|
33
|
+
viewStatus: {
|
|
34
|
+
width: 65,
|
|
35
|
+
paddingHorizontal: 8,
|
|
36
|
+
paddingVertical: 4,
|
|
37
|
+
borderRadius: 54,
|
|
38
|
+
backgroundColor: Colors.White,
|
|
39
|
+
borderColor: Colors.Neutral.Neutral3,
|
|
40
|
+
borderWidth: 1,
|
|
41
|
+
},
|
|
42
|
+
textStatus: {
|
|
43
|
+
fontStyle: 'normal',
|
|
44
|
+
fontWeight: 600,
|
|
45
|
+
letterSpacing: 0.04,
|
|
46
|
+
fontSize: 12,
|
|
47
|
+
lineHeight: 16,
|
|
48
|
+
textTransform: 'uppercase',
|
|
49
|
+
textAlign: 'center',
|
|
50
|
+
},
|
|
51
|
+
listGateways: {
|
|
52
|
+
width: '100%',
|
|
53
|
+
paddingVertical: 16,
|
|
54
|
+
},
|
|
55
|
+
textOnline: {
|
|
56
|
+
fontStyle: 'bold',
|
|
57
|
+
},
|
|
58
|
+
viewOnline: {
|
|
59
|
+
backgroundColor: Colors.LightGreen,
|
|
60
|
+
borderWidth: 0,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { axiosGet } from '../../../utils/Apis/axios';
|
|
3
|
+
import API from '../../../configs/API';
|
|
4
|
+
|
|
5
|
+
export default () => {
|
|
6
|
+
const [loading, setLoading] = useState(true);
|
|
7
|
+
const [gateways, setGateways] = useState([]);
|
|
8
|
+
const [nameSearch, setNameSearch] = useState('');
|
|
9
|
+
const [gatewayDetail, setGatewayDetail] = useState([]);
|
|
10
|
+
|
|
11
|
+
const fetchDataDetail = useCallback(async () => {
|
|
12
|
+
setLoading(true);
|
|
13
|
+
const { success, data } = await axiosGet(API.DEV_MODE.GATEWAY.LIST());
|
|
14
|
+
if (success) {
|
|
15
|
+
setGatewayDetail(data?.results || []);
|
|
16
|
+
}
|
|
17
|
+
setLoading(false);
|
|
18
|
+
}, []);
|
|
19
|
+
|
|
20
|
+
const fetchDataGateways = useCallback(async () => {
|
|
21
|
+
setLoading(true);
|
|
22
|
+
const { success, data } = await axiosGet(API.DEV_MODE.GATEWAY.LIST());
|
|
23
|
+
if (success) {
|
|
24
|
+
setGateways(data?.results || []);
|
|
25
|
+
}
|
|
26
|
+
setLoading(false);
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
gateways,
|
|
31
|
+
fetchDataGateways,
|
|
32
|
+
loading,
|
|
33
|
+
setNameSearch,
|
|
34
|
+
nameSearch,
|
|
35
|
+
gatewayDetail,
|
|
36
|
+
fetchDataDetail,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
@@ -1,8 +1,80 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { View } from 'react-native';
|
|
1
|
+
import React, { useCallback, memo, useEffect, useMemo } from 'react';
|
|
2
|
+
import { View, FlatList } from 'react-native';
|
|
3
|
+
import { useNavigation, useIsFocused } from '@react-navigation/native';
|
|
4
|
+
import { AccessibilityLabel } from '../../configs/Constants';
|
|
5
|
+
import { Colors } from '../../configs';
|
|
6
|
+
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
7
|
+
import { Text } from '../../commons';
|
|
8
|
+
import styles from './styles';
|
|
9
|
+
import { Search, EmptyComponent } from '../../commons/DevMode';
|
|
10
|
+
import GatewayItem from './components/GatewayItem';
|
|
11
|
+
import useGateway from './hooks/useGateway';
|
|
12
|
+
import Routes from '../../utils/Route';
|
|
3
13
|
|
|
4
|
-
const Gateway = () => {
|
|
5
|
-
|
|
14
|
+
const Gateway = ({ route }) => {
|
|
15
|
+
const t = useTranslations();
|
|
16
|
+
const isFocused = useIsFocused();
|
|
17
|
+
const { navigate } = useNavigation();
|
|
18
|
+
const { gateways, fetchDataGateways, setNameSearch, nameSearch } =
|
|
19
|
+
useGateway();
|
|
20
|
+
|
|
21
|
+
const goGoDetail = useCallback(
|
|
22
|
+
(item) => () => {
|
|
23
|
+
navigate(Routes.GatewayDetail, { item });
|
|
24
|
+
},
|
|
25
|
+
[navigate]
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const gatewaysSearched = useMemo(() => {
|
|
29
|
+
return gateways.filter(
|
|
30
|
+
(item) =>
|
|
31
|
+
item?.name?.toLowerCase()?.indexOf(nameSearch?.toLowerCase()) > -1
|
|
32
|
+
);
|
|
33
|
+
}, [gateways, nameSearch]);
|
|
34
|
+
|
|
35
|
+
const renderListEmptyComponent = useMemo(() => {
|
|
36
|
+
return (
|
|
37
|
+
<View style={styles.wrapEmpty}>
|
|
38
|
+
<EmptyComponent
|
|
39
|
+
text1={t('no_gateway_yet')}
|
|
40
|
+
text2={t('you_dont_have_any_gateway')}
|
|
41
|
+
/>
|
|
42
|
+
</View>
|
|
43
|
+
);
|
|
44
|
+
}, [t]);
|
|
45
|
+
|
|
46
|
+
const renderItem = useCallback(
|
|
47
|
+
({ item, index }) => (
|
|
48
|
+
<GatewayItem item={item} key={index} onPress={goGoDetail(item)} />
|
|
49
|
+
),
|
|
50
|
+
[goGoDetail]
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
isFocused && fetchDataGateways();
|
|
55
|
+
}, [isFocused, fetchDataGateways]);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<View
|
|
59
|
+
style={styles.wrap}
|
|
60
|
+
accessibilityLabel={AccessibilityLabel.LIST_GATEWAY}
|
|
61
|
+
>
|
|
62
|
+
<Text type="H3" bold color={Colors.Gray9} style={styles.textTitle}>
|
|
63
|
+
{t('gateway')}
|
|
64
|
+
</Text>
|
|
65
|
+
<Search onSearch={setNameSearch} />
|
|
66
|
+
<FlatList
|
|
67
|
+
columnWrapperStyle={styles.spaceBetween}
|
|
68
|
+
contentContainerStyle={styles.contentContainerStyle}
|
|
69
|
+
keyExtractor={(item) => item?.id}
|
|
70
|
+
data={gatewaysSearched}
|
|
71
|
+
renderItem={renderItem}
|
|
72
|
+
extraData={gatewaysSearched}
|
|
73
|
+
ListEmptyComponent={renderListEmptyComponent}
|
|
74
|
+
numColumns={2}
|
|
75
|
+
/>
|
|
76
|
+
</View>
|
|
77
|
+
);
|
|
6
78
|
};
|
|
7
79
|
|
|
8
|
-
export default Gateway;
|
|
80
|
+
export default memo(Gateway);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Colors, Constants } from '../../configs';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
wrap: {
|
|
6
|
+
flex: 1,
|
|
7
|
+
paddingTop: 16,
|
|
8
|
+
paddingHorizontal: 16,
|
|
9
|
+
backgroundColor: Colors.Gray2,
|
|
10
|
+
},
|
|
11
|
+
textTitle: {
|
|
12
|
+
fontStyle: 'normal',
|
|
13
|
+
fontWeight: 650,
|
|
14
|
+
fontSize: 28,
|
|
15
|
+
lineHeight: 36,
|
|
16
|
+
marignBottom: 16,
|
|
17
|
+
},
|
|
18
|
+
scrollview: {
|
|
19
|
+
flex: 1,
|
|
20
|
+
},
|
|
21
|
+
contentContainerStyle: {
|
|
22
|
+
flexDirection: 'column',
|
|
23
|
+
},
|
|
24
|
+
wrapEmpty: {
|
|
25
|
+
justifyContent: 'center',
|
|
26
|
+
alignItems: 'center',
|
|
27
|
+
height: Constants.height - 330,
|
|
28
|
+
},
|
|
29
|
+
spaceBetween: {
|
|
30
|
+
justifyContent: 'space-between',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -999,6 +999,10 @@
|
|
|
999
999
|
"no_template_yet": "No Template yet",
|
|
1000
1000
|
"add_your_template": "Add your template at web app",
|
|
1001
1001
|
"edit_template": "Edit template",
|
|
1002
|
+
"no_gateway_yet": "No Gateway yet",
|
|
1003
|
+
"you_dont_have_any_gateway": "You don't have any gateway",
|
|
1004
|
+
"gateway_information": "Gateway information",
|
|
1005
|
+
"reboot": "Reboot",
|
|
1002
1006
|
"information": "Information",
|
|
1003
1007
|
"gateway_list": "Gateway list",
|
|
1004
1008
|
"delete_template": "Delete template",
|
|
@@ -998,6 +998,10 @@
|
|
|
998
998
|
"no_template_yet": "Chưa có mẫu nào",
|
|
999
999
|
"add_your_template": "Thêm mẫu của bạn trên web",
|
|
1000
1000
|
"edit_template": "Chỉnh sửa mẫu",
|
|
1001
|
+
"no_gateway_yet": "Chưa có cổng nào",
|
|
1002
|
+
"you_dont_have_any_gateway": "Bạn không có bất kỳ cổng nào",
|
|
1003
|
+
"gateway_information": "Thông tin cổng",
|
|
1004
|
+
"reboot": "Khởi động lại",
|
|
1001
1005
|
"information": "Thông tin",
|
|
1002
1006
|
"gateway_list": "Danh sách cổng vào",
|
|
1003
1007
|
"delete_template": "Xóa mẫu",
|
package/src/utils/Route/index.js
CHANGED