@eohjsc/react-native-smart-city 0.4.97 → 0.4.99
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/ActionGroup/OnOffTemplate/index.js +21 -17
- package/src/commons/ActionGroup/TerminalBoxTemplate.js +182 -0
- package/src/commons/ActionGroup/TerminalBoxTemplateStyle.js +54 -0
- package/src/commons/ActionGroup/TextBoxTemplate.js +11 -5
- package/src/commons/ActionGroup/__test__/TerminalBoxTemplate.test.js +232 -0
- package/src/commons/ActionGroup/__test__/TextBoxTemplate.test.js +1 -1
- package/src/commons/ActionGroup/index.js +3 -1
- package/src/commons/Device/ConnectedViewHeader.js +1 -2
- package/src/commons/OneTapTemplate/SliderRangeActionTemplate.js +3 -3
- package/src/commons/OneTapTemplate/TerminalBoxActionTemplate.js +87 -0
- package/src/commons/OneTapTemplate/TextBoxActionTemplate.js +2 -2
- package/src/commons/OneTapTemplate/__test__/SliderRangeActionTemplate.test.js +1 -1
- package/src/commons/OneTapTemplate/__test__/TerminalBoxActionTemplate.test.js +118 -0
- package/src/commons/Sharing/WrapHeaderScrollable.js +1 -1
- package/src/commons/UnitSummary/ConfigHistoryChart/index.js +15 -16
- package/src/configs/API.js +1 -0
- package/src/configs/AccessibilityLabel.js +2 -0
- package/src/configs/Colors.js +1 -0
- package/src/screens/Automate/AddNewAction/ChooseAction.js +10 -0
- package/src/screens/Automate/ScriptDetail/index.js +2 -1
- package/src/screens/Device/components/SensorDisplayItem.js +1 -0
- package/src/screens/Device/detail.js +23 -18
- package/src/utils/I18n/translations/en.js +1 -1
- package/src/utils/I18n/translations/vi.js +1 -1
package/package.json
CHANGED
|
@@ -9,8 +9,6 @@ import OnOffButtonTemplate from './OnOffButtonTemplate';
|
|
|
9
9
|
import OnOffSimpleTemplate from './OnOffSimpleTemplate';
|
|
10
10
|
import styles from './styles';
|
|
11
11
|
|
|
12
|
-
let temp;
|
|
13
|
-
|
|
14
12
|
const getComponent = (template) => {
|
|
15
13
|
switch (template) {
|
|
16
14
|
case 'on_off_button_action_template': // todo refactor later with backend
|
|
@@ -53,15 +51,18 @@ const OnOffTemplate = memo(({ item = {}, doAction, sensor = {} }) => {
|
|
|
53
51
|
|
|
54
52
|
const [isOn, setIsOn] = useState(false);
|
|
55
53
|
const [tempIsOn, setTempIsOn] = useState(getIsOnValue());
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
const [timeoutId, setTimeoutId] = useState();
|
|
55
|
+
const [needUpdateTempIsOn, setNeedUpdateTempIsOn] = useState(false);
|
|
56
|
+
|
|
57
|
+
const refreshTempIsOn = useCallback(() => {
|
|
58
|
+
const timeout = setTimeout(() => {
|
|
59
|
+
setNeedUpdateTempIsOn(true);
|
|
60
|
+
}, 7000);
|
|
61
|
+
setTimeoutId(timeout);
|
|
61
62
|
}, []);
|
|
62
63
|
|
|
63
64
|
const triggerAction = useCallback(async () => {
|
|
64
|
-
const action_data =
|
|
65
|
+
const action_data = tempIsOn ? action_off_data : action_on_data;
|
|
65
66
|
if (!action_data) {
|
|
66
67
|
return;
|
|
67
68
|
}
|
|
@@ -76,10 +77,11 @@ const OnOffTemplate = memo(({ item = {}, doAction, sensor = {} }) => {
|
|
|
76
77
|
config_value: isOn ? 0 : 1,
|
|
77
78
|
};
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
+
clearTimeout(timeoutId);
|
|
80
81
|
await doAction(action_data, data);
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
setTempIsOn((prev) => !prev);
|
|
83
|
+
setNeedUpdateTempIsOn(false);
|
|
84
|
+
refreshTempIsOn();
|
|
83
85
|
if (
|
|
84
86
|
is_managed_by_backend &&
|
|
85
87
|
config &&
|
|
@@ -88,15 +90,17 @@ const OnOffTemplate = memo(({ item = {}, doAction, sensor = {} }) => {
|
|
|
88
90
|
watchMultiConfigs([config]);
|
|
89
91
|
}
|
|
90
92
|
}, [
|
|
91
|
-
|
|
93
|
+
tempIsOn,
|
|
92
94
|
action_off_data,
|
|
93
95
|
action_on_data,
|
|
94
|
-
device_type,
|
|
95
|
-
is_managed_by_backend,
|
|
96
96
|
allow_config_store_value,
|
|
97
97
|
config,
|
|
98
|
+
device_type,
|
|
99
|
+
timeoutId,
|
|
98
100
|
doAction,
|
|
99
|
-
|
|
101
|
+
refreshTempIsOn,
|
|
102
|
+
is_managed_by_backend,
|
|
103
|
+
isOn,
|
|
100
104
|
]);
|
|
101
105
|
|
|
102
106
|
useUnwatchLGDeviceConfigControl(sensor, [config]);
|
|
@@ -106,8 +110,8 @@ const OnOffTemplate = memo(({ item = {}, doAction, sensor = {} }) => {
|
|
|
106
110
|
}, [getIsOnValue]);
|
|
107
111
|
|
|
108
112
|
useEffect(() => {
|
|
109
|
-
|
|
110
|
-
}, [
|
|
113
|
+
needUpdateTempIsOn && setTempIsOn(getIsOnValue());
|
|
114
|
+
}, [getIsOnValue, needUpdateTempIsOn]);
|
|
111
115
|
|
|
112
116
|
useEffect(() => {
|
|
113
117
|
if (device_type !== DEVICE_TYPE.LG_THINQ) {
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import { View, TouchableOpacity } from 'react-native';
|
|
9
|
+
import { IconOutline } from '@ant-design/icons-react-native';
|
|
10
|
+
import Text from '../../commons/Text';
|
|
11
|
+
import { useConfigGlobalState } from '../../iot/states';
|
|
12
|
+
import styles from './TerminalBoxTemplateStyle';
|
|
13
|
+
import _TextInput from '../Form/TextInput';
|
|
14
|
+
import AccessibilityLabel from '../../configs/AccessibilityLabel';
|
|
15
|
+
import moment from 'moment';
|
|
16
|
+
import { useFetchConfigHistory } from '../UnitSummary/ConfigHistoryChart';
|
|
17
|
+
import { ScrollView } from 'react-native';
|
|
18
|
+
import { API } from '../../configs';
|
|
19
|
+
|
|
20
|
+
const useNewMessage = (
|
|
21
|
+
newValue,
|
|
22
|
+
latestUpdatedFromMessage,
|
|
23
|
+
setAllMessages,
|
|
24
|
+
setLatestUpdatedFromMessage,
|
|
25
|
+
type
|
|
26
|
+
) => {
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (
|
|
29
|
+
!newValue ||
|
|
30
|
+
!newValue.last_updated ||
|
|
31
|
+
moment(newValue.last_updated).isSameOrBefore(latestUpdatedFromMessage)
|
|
32
|
+
) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
setAllMessages((prev) => [
|
|
36
|
+
...prev,
|
|
37
|
+
{ x: moment(), y: newValue.value, type },
|
|
38
|
+
]);
|
|
39
|
+
setLatestUpdatedFromMessage(moment(newValue.last_updated));
|
|
40
|
+
}, [
|
|
41
|
+
newValue,
|
|
42
|
+
latestUpdatedFromMessage,
|
|
43
|
+
setAllMessages,
|
|
44
|
+
setLatestUpdatedFromMessage,
|
|
45
|
+
type,
|
|
46
|
+
]);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const TerminalBoxTemplate = ({ item, doAction, isWidgetOrder }) => {
|
|
50
|
+
const { label, configuration } = item;
|
|
51
|
+
const { action_data, from_config, to_config } = configuration;
|
|
52
|
+
const [configValues] = useConfigGlobalState('configValues');
|
|
53
|
+
const [value, setValue] = useState();
|
|
54
|
+
const [allMessages, setAllMessages] = useState([]);
|
|
55
|
+
const [latestUpdatedFromMessage, setLatestUpdatedFromMessage] = useState(
|
|
56
|
+
moment()
|
|
57
|
+
);
|
|
58
|
+
const [latestUpdatedToMessage, setLatestUpdatedToMessage] = useState(
|
|
59
|
+
moment()
|
|
60
|
+
);
|
|
61
|
+
const scrollViewRef = useRef();
|
|
62
|
+
const sendCommand = useCallback(() => {
|
|
63
|
+
doAction(action_data, JSON.stringify({ value: value }));
|
|
64
|
+
setValue('');
|
|
65
|
+
}, [doAction, action_data, value]);
|
|
66
|
+
|
|
67
|
+
const onInputChange = (e) => {
|
|
68
|
+
setValue(e);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const fromValue = useMemo(() => {
|
|
72
|
+
return configValues[from_config?.id];
|
|
73
|
+
}, [configValues, from_config]);
|
|
74
|
+
|
|
75
|
+
const toValue = useMemo(() => {
|
|
76
|
+
return configValues[to_config?.id];
|
|
77
|
+
}, [configValues, to_config]);
|
|
78
|
+
|
|
79
|
+
const configs = useMemo(() => {
|
|
80
|
+
if (!from_config || !to_config) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
return [from_config, to_config];
|
|
84
|
+
}, [from_config, to_config]);
|
|
85
|
+
|
|
86
|
+
const scrollToBottom = () => {
|
|
87
|
+
setTimeout(() => {
|
|
88
|
+
scrollViewRef.current.scrollToEnd({ animated: true });
|
|
89
|
+
}, 100);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const setMessages = useCallback(
|
|
93
|
+
(configMessages) => {
|
|
94
|
+
let messages = [];
|
|
95
|
+
configMessages.map((configMessage) => {
|
|
96
|
+
const type = configMessage.id === from_config.id ? 'from' : 'to';
|
|
97
|
+
messages = [
|
|
98
|
+
...messages,
|
|
99
|
+
...configMessage.data.map((item) => {
|
|
100
|
+
return { x: item.x, y: item.y, type };
|
|
101
|
+
}),
|
|
102
|
+
];
|
|
103
|
+
});
|
|
104
|
+
messages = messages.sort((a, b) => {
|
|
105
|
+
return moment(a.x).diff(moment(b.x));
|
|
106
|
+
});
|
|
107
|
+
setAllMessages(messages);
|
|
108
|
+
scrollToBottom();
|
|
109
|
+
},
|
|
110
|
+
[from_config]
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const fetchDataDisplayHistory = useFetchConfigHistory(
|
|
114
|
+
configs,
|
|
115
|
+
setMessages,
|
|
116
|
+
API.CONFIG.DISPLAY_HISTORY_V4()
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
fetchDataDisplayHistory(moment().subtract(7, 'days'), moment());
|
|
121
|
+
}, [fetchDataDisplayHistory]);
|
|
122
|
+
|
|
123
|
+
useNewMessage(
|
|
124
|
+
fromValue,
|
|
125
|
+
latestUpdatedFromMessage,
|
|
126
|
+
setAllMessages,
|
|
127
|
+
setLatestUpdatedFromMessage,
|
|
128
|
+
'from'
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
useNewMessage(
|
|
132
|
+
toValue,
|
|
133
|
+
latestUpdatedToMessage,
|
|
134
|
+
setAllMessages,
|
|
135
|
+
setLatestUpdatedToMessage,
|
|
136
|
+
'to'
|
|
137
|
+
);
|
|
138
|
+
return (
|
|
139
|
+
<View style={(isWidgetOrder && styles.wrapOrderItem) || styles.wrap}>
|
|
140
|
+
<View>
|
|
141
|
+
<Text type="H4">{label}</Text>
|
|
142
|
+
</View>
|
|
143
|
+
<ScrollView
|
|
144
|
+
ref={scrollViewRef}
|
|
145
|
+
onContentSizeChange={() =>
|
|
146
|
+
scrollViewRef.current.scrollToEnd({ animated: true })
|
|
147
|
+
}
|
|
148
|
+
style={styles.scrollView}
|
|
149
|
+
>
|
|
150
|
+
{allMessages.map((item, index) => {
|
|
151
|
+
return (
|
|
152
|
+
<View
|
|
153
|
+
key={index}
|
|
154
|
+
style={item.type === 'to' ? styles.to : styles.from}
|
|
155
|
+
>
|
|
156
|
+
<Text>{item.y}</Text>
|
|
157
|
+
<Text>{moment(item.x).format('DD/MM/YYYY HH:mm:ss')}</Text>
|
|
158
|
+
</View>
|
|
159
|
+
);
|
|
160
|
+
})}
|
|
161
|
+
</ScrollView>
|
|
162
|
+
<View style={styles.iconAndText}>
|
|
163
|
+
<_TextInput
|
|
164
|
+
wrapStyle={styles.wrapInputStyle}
|
|
165
|
+
value={value}
|
|
166
|
+
onChange={onInputChange}
|
|
167
|
+
maxLength={255}
|
|
168
|
+
/>
|
|
169
|
+
<TouchableOpacity
|
|
170
|
+
style={styles.iconAndTextOption}
|
|
171
|
+
onPress={sendCommand}
|
|
172
|
+
accessibilityLabel={AccessibilityLabel.TERMINAL_BOX_BUTTON_SEND}
|
|
173
|
+
disabled={!value}
|
|
174
|
+
>
|
|
175
|
+
<IconOutline name="send" size={20} />
|
|
176
|
+
</TouchableOpacity>
|
|
177
|
+
</View>
|
|
178
|
+
</View>
|
|
179
|
+
);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export default TerminalBoxTemplate;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Colors } from '../../configs';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
wrapOrderItem: {
|
|
6
|
+
flex: 1,
|
|
7
|
+
flexDirection: 'row',
|
|
8
|
+
justifyContent: 'space-between',
|
|
9
|
+
padding: 16,
|
|
10
|
+
},
|
|
11
|
+
wrap: {
|
|
12
|
+
padding: 16,
|
|
13
|
+
marginHorizontal: 16,
|
|
14
|
+
marginBottom: 16,
|
|
15
|
+
borderWidth: 1,
|
|
16
|
+
borderColor: Colors.Gray4,
|
|
17
|
+
borderRadius: 10,
|
|
18
|
+
flex: 1,
|
|
19
|
+
flexDirection: 'column',
|
|
20
|
+
},
|
|
21
|
+
iconAndText: {
|
|
22
|
+
flexDirection: 'row',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
width: '100%',
|
|
25
|
+
},
|
|
26
|
+
iconAndTextOption: {
|
|
27
|
+
marginTop: 25,
|
|
28
|
+
},
|
|
29
|
+
wrapInputStyle: {
|
|
30
|
+
marginRight: 14,
|
|
31
|
+
width: '90%',
|
|
32
|
+
},
|
|
33
|
+
scrollView: {
|
|
34
|
+
marginTop: 13,
|
|
35
|
+
height: 250,
|
|
36
|
+
},
|
|
37
|
+
to: {
|
|
38
|
+
padding: 5,
|
|
39
|
+
borderWidth: 1,
|
|
40
|
+
borderColor: Colors.Gray4,
|
|
41
|
+
borderRadius: 10,
|
|
42
|
+
marginBottom: 5,
|
|
43
|
+
},
|
|
44
|
+
from: {
|
|
45
|
+
alignItems: 'flex-end',
|
|
46
|
+
marginRight: 0,
|
|
47
|
+
backgroundColor: Colors.Blue18,
|
|
48
|
+
padding: 5,
|
|
49
|
+
borderWidth: 1,
|
|
50
|
+
borderColor: Colors.Gray4,
|
|
51
|
+
borderRadius: 10,
|
|
52
|
+
marginBottom: 5,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
2
|
import { View, TouchableOpacity } from 'react-native';
|
|
3
3
|
import { IconOutline } from '@ant-design/icons-react-native';
|
|
4
4
|
import Text from '../../commons/Text';
|
|
@@ -10,14 +10,15 @@ import styles from './TextBoxTemplateStyle';
|
|
|
10
10
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
11
11
|
import _TextInput from '../Form/TextInput';
|
|
12
12
|
import AccessibilityLabel from '../../configs/AccessibilityLabel';
|
|
13
|
+
import useKeyboardAnimated from '../../hooks/Explore/useKeyboardAnimated';
|
|
13
14
|
|
|
14
15
|
const TextBoxTemplate = ({ item, doAction, isWidgetOrder }) => {
|
|
15
16
|
const t = useTranslations();
|
|
17
|
+
const transY = useKeyboardAnimated();
|
|
16
18
|
const { label, configuration } = item;
|
|
17
19
|
const { action_data, config } = configuration;
|
|
18
20
|
const [configValues] = useConfigGlobalState('configValues');
|
|
19
21
|
const [value, setValue] = useState();
|
|
20
|
-
|
|
21
22
|
const { stateAlert, hideAlertAction, onShowAlert } = useDropdownAction();
|
|
22
23
|
|
|
23
24
|
const onDone = useCallback(() => {
|
|
@@ -25,9 +26,13 @@ const TextBoxTemplate = ({ item, doAction, isWidgetOrder }) => {
|
|
|
25
26
|
hideAlertAction();
|
|
26
27
|
}, [doAction, action_data, value, hideAlertAction]);
|
|
27
28
|
|
|
28
|
-
const onInputChange = (
|
|
29
|
-
setValue(
|
|
29
|
+
const onInputChange = (e) => {
|
|
30
|
+
setValue(e);
|
|
30
31
|
};
|
|
32
|
+
const valueText = useMemo(() => {
|
|
33
|
+
return configValues[config.id]?.value || '';
|
|
34
|
+
}, [config.id, configValues]);
|
|
35
|
+
|
|
31
36
|
return (
|
|
32
37
|
<View style={(isWidgetOrder && styles.wrapOrderItem) || styles.wrap}>
|
|
33
38
|
<View>
|
|
@@ -35,7 +40,7 @@ const TextBoxTemplate = ({ item, doAction, isWidgetOrder }) => {
|
|
|
35
40
|
</View>
|
|
36
41
|
|
|
37
42
|
<View style={styles.iconAndText}>
|
|
38
|
-
<Text type="H4">{
|
|
43
|
+
<Text type="H4">{valueText}</Text>
|
|
39
44
|
<TouchableOpacity
|
|
40
45
|
style={styles.iconAndTextOption}
|
|
41
46
|
onPress={onShowAlert}
|
|
@@ -53,6 +58,7 @@ const TextBoxTemplate = ({ item, doAction, isWidgetOrder }) => {
|
|
|
53
58
|
rightButtonTitle={stateAlert.rightButton}
|
|
54
59
|
rightButtonClick={onDone}
|
|
55
60
|
rightDisabled={!value}
|
|
61
|
+
transY={transY}
|
|
56
62
|
>
|
|
57
63
|
<_TextInput
|
|
58
64
|
wrapStyle={styles.wrapInputStyle}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TouchableOpacity, View } from 'react-native';
|
|
3
|
+
import { act, create } from 'react-test-renderer';
|
|
4
|
+
import { watchMultiConfigs } from '../../../iot/Monitor';
|
|
5
|
+
import { AccessibilityLabel } from '../../../configs/Constants';
|
|
6
|
+
import { SCProvider } from '../../../context';
|
|
7
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
8
|
+
|
|
9
|
+
import _TextInput from '../../Form/TextInput';
|
|
10
|
+
import { API, Colors } from '../../../configs';
|
|
11
|
+
import TerminalBoxTemplate from '../TerminalBoxTemplate';
|
|
12
|
+
import MockAdapter from 'axios-mock-adapter';
|
|
13
|
+
import api from '../../../utils/Apis/axios';
|
|
14
|
+
import { ScrollView } from 'react-native';
|
|
15
|
+
|
|
16
|
+
const mock = new MockAdapter(api.axiosInstance);
|
|
17
|
+
const wrapComponent = (item, mockDoAction, isWidgetOrder = false) => (
|
|
18
|
+
<SCProvider initState={mockSCStore({})}>
|
|
19
|
+
<TerminalBoxTemplate
|
|
20
|
+
item={item}
|
|
21
|
+
doAction={mockDoAction}
|
|
22
|
+
isWidgetOrder={isWidgetOrder}
|
|
23
|
+
/>
|
|
24
|
+
</SCProvider>
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
jest.mock('../../../iot/Monitor');
|
|
28
|
+
|
|
29
|
+
jest.mock('../../../iot/states', () => ({
|
|
30
|
+
useConfigGlobalState: () => [{ 5: { value: 2 } }, null],
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
jest.mock('@react-navigation/native', () => {
|
|
34
|
+
return {
|
|
35
|
+
...jest.requireActual('@react-navigation/native'),
|
|
36
|
+
useFocusEffect: jest.fn(),
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('Test TerminalBoxTemplate', () => {
|
|
41
|
+
let displayItem;
|
|
42
|
+
let sensor;
|
|
43
|
+
let wrapper;
|
|
44
|
+
let data;
|
|
45
|
+
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
watchMultiConfigs.mockClear();
|
|
48
|
+
jest.useFakeTimers();
|
|
49
|
+
displayItem = {
|
|
50
|
+
id: 33909,
|
|
51
|
+
configuration_error: null,
|
|
52
|
+
is_configuration_ready: true,
|
|
53
|
+
configuration: {
|
|
54
|
+
from_config: {
|
|
55
|
+
id: 35490,
|
|
56
|
+
sensor_id: 18310,
|
|
57
|
+
chip_id: 4129,
|
|
58
|
+
color: '#000000',
|
|
59
|
+
standard: '',
|
|
60
|
+
measure: '',
|
|
61
|
+
unit: '',
|
|
62
|
+
data: [
|
|
63
|
+
{
|
|
64
|
+
x: '2024-02-28T07:53:57.828537Z',
|
|
65
|
+
y: null,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
to_config: {
|
|
70
|
+
id: 35491,
|
|
71
|
+
sensor_id: 18310,
|
|
72
|
+
chip_id: 4129,
|
|
73
|
+
color: '#000000',
|
|
74
|
+
standard: '',
|
|
75
|
+
measure: '',
|
|
76
|
+
unit: '',
|
|
77
|
+
data: [
|
|
78
|
+
{
|
|
79
|
+
x: '2024-02-28T07:53:57.828537Z',
|
|
80
|
+
y: null,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
action: 'e8d1de74-7609-4e04-b63d-e6a19389d608',
|
|
85
|
+
action_data: {
|
|
86
|
+
id: 33283,
|
|
87
|
+
name: 'String V4',
|
|
88
|
+
key: 'e8d1de74-7609-4e04-b63d-e6a19389d608',
|
|
89
|
+
icon: '',
|
|
90
|
+
icon_kit: null,
|
|
91
|
+
color: '#00979D',
|
|
92
|
+
home_assistant_action: null,
|
|
93
|
+
lg_action: null,
|
|
94
|
+
arduino_action: {
|
|
95
|
+
id: 8025,
|
|
96
|
+
pin: null,
|
|
97
|
+
value: 0,
|
|
98
|
+
},
|
|
99
|
+
modbus_action: null,
|
|
100
|
+
zigbee_action: null,
|
|
101
|
+
command_prefer_over_internet: true,
|
|
102
|
+
command_prefer_over_bluetooth: false,
|
|
103
|
+
command_prefer_over_googlehome: false,
|
|
104
|
+
allow_config_store_value_id: null,
|
|
105
|
+
end_device: {
|
|
106
|
+
id: 10161,
|
|
107
|
+
name: 'Terminal Box',
|
|
108
|
+
device_type: '',
|
|
109
|
+
},
|
|
110
|
+
sensor_id: 18310,
|
|
111
|
+
chip_id: 4129,
|
|
112
|
+
is_only_bluetooth: false,
|
|
113
|
+
googlehome_action: null,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
template: 'TerminalBoxTemplate',
|
|
117
|
+
order: 0,
|
|
118
|
+
label: 'String Box',
|
|
119
|
+
};
|
|
120
|
+
sensor = {
|
|
121
|
+
name: 'Sensor name',
|
|
122
|
+
is_managed_by_backend: false,
|
|
123
|
+
};
|
|
124
|
+
data = {
|
|
125
|
+
configs: [
|
|
126
|
+
{
|
|
127
|
+
id: 35490,
|
|
128
|
+
head: [
|
|
129
|
+
{
|
|
130
|
+
x: '2024-02-28T07:53:57.787788Z',
|
|
131
|
+
y: null,
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
tail: [
|
|
135
|
+
{
|
|
136
|
+
x: '2024-02-29T07:30:01.940166Z',
|
|
137
|
+
y: null,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
middle: {
|
|
141
|
+
ready: [],
|
|
142
|
+
not_ready: [],
|
|
143
|
+
channel: 'cache-4e37a226-c61c-4708-a00c-c22bd192b845',
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 35491,
|
|
148
|
+
head: [
|
|
149
|
+
{
|
|
150
|
+
x: '2024-02-28T07:53:57.828537Z',
|
|
151
|
+
y: null,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
tail: [
|
|
155
|
+
{
|
|
156
|
+
x: '2024-02-29T01:42:14.488233Z',
|
|
157
|
+
y: null,
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
middle: {
|
|
161
|
+
ready: [],
|
|
162
|
+
not_ready: [],
|
|
163
|
+
channel: 'cache-2d0806d7-5c15-4af0-8443-6ec6b54ba9cf',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('render template', async () => {
|
|
171
|
+
const mockDoAction = jest.fn();
|
|
172
|
+
mock.onGet(API.CONFIG.DISPLAY_HISTORY_V4()).reply(200, data);
|
|
173
|
+
await act(async () => {
|
|
174
|
+
wrapper = await create(wrapComponent(displayItem, mockDoAction));
|
|
175
|
+
});
|
|
176
|
+
jest.runAllTimers();
|
|
177
|
+
|
|
178
|
+
const instance = wrapper.root;
|
|
179
|
+
const views = instance.findAllByType(View);
|
|
180
|
+
|
|
181
|
+
expect(views[0].props.style).toEqual({
|
|
182
|
+
padding: 16,
|
|
183
|
+
marginHorizontal: 16,
|
|
184
|
+
marginBottom: 16,
|
|
185
|
+
borderWidth: 1,
|
|
186
|
+
borderColor: Colors.Gray4,
|
|
187
|
+
borderRadius: 10,
|
|
188
|
+
flex: 1,
|
|
189
|
+
flexDirection: 'column',
|
|
190
|
+
});
|
|
191
|
+
const scroll = instance.findByType(ScrollView);
|
|
192
|
+
await act(async () => {
|
|
193
|
+
await scroll.props.onContentSizeChange();
|
|
194
|
+
});
|
|
195
|
+
const textInput = instance.findByType(_TextInput);
|
|
196
|
+
await act(async () => {
|
|
197
|
+
await textInput.props.onChange('123');
|
|
198
|
+
});
|
|
199
|
+
const buttonSend = instance.find(
|
|
200
|
+
(el) =>
|
|
201
|
+
el.props.accessibilityLabel ===
|
|
202
|
+
AccessibilityLabel.TERMINAL_BOX_BUTTON_SEND &&
|
|
203
|
+
el.type === TouchableOpacity
|
|
204
|
+
);
|
|
205
|
+
await act(async () => {
|
|
206
|
+
await buttonSend.props.onPress();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
expect(mockDoAction).toHaveBeenCalledWith(
|
|
210
|
+
displayItem.configuration.action_data,
|
|
211
|
+
JSON.stringify({ value: '123' })
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
it('render template isWidgetOrder change position', async () => {
|
|
215
|
+
const mockDoAction = jest.fn();
|
|
216
|
+
sensor.is_managed_by_backend = true;
|
|
217
|
+
displayItem.configuration.from_config = null;
|
|
218
|
+
mock.onGet(API.CONFIG.DISPLAY_HISTORY_V4()).reply(200, data);
|
|
219
|
+
await act(async () => {
|
|
220
|
+
wrapper = await create(wrapComponent(displayItem, mockDoAction, true));
|
|
221
|
+
});
|
|
222
|
+
const instance = wrapper.root;
|
|
223
|
+
const views = instance.findAllByType(View);
|
|
224
|
+
|
|
225
|
+
expect(views[0].props.style).toEqual({
|
|
226
|
+
flex: 1,
|
|
227
|
+
flexDirection: 'row',
|
|
228
|
+
justifyContent: 'space-between',
|
|
229
|
+
padding: 16,
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
});
|
|
@@ -14,6 +14,7 @@ import OnOffSmartLock from './OnOffSmartLock/OnOffSmartLock';
|
|
|
14
14
|
import TwoButtonTemplate from './TwoButtonTemplate';
|
|
15
15
|
import SwitchButtonTemplate from './OnOffTemplate/SwitchButtonTemplate';
|
|
16
16
|
import TextBoxTemplate from './TextBoxTemplate';
|
|
17
|
+
import TerminalBoxTemplate from './TerminalBoxTemplate';
|
|
17
18
|
|
|
18
19
|
export const getActionComponent = (template) => {
|
|
19
20
|
switch (template) {
|
|
@@ -49,6 +50,8 @@ export const getActionComponent = (template) => {
|
|
|
49
50
|
return SwitchButtonTemplate;
|
|
50
51
|
case 'TextBoxTemplate':
|
|
51
52
|
return TextBoxTemplate;
|
|
53
|
+
case 'TerminalBoxTemplate':
|
|
54
|
+
return TerminalBoxTemplate;
|
|
52
55
|
default:
|
|
53
56
|
return null;
|
|
54
57
|
}
|
|
@@ -56,7 +59,6 @@ export const getActionComponent = (template) => {
|
|
|
56
59
|
|
|
57
60
|
const ActionGroup = (params = {}) => {
|
|
58
61
|
const { item } = params;
|
|
59
|
-
|
|
60
62
|
const ButtonGroupComponent = useMemo(() => {
|
|
61
63
|
return getActionComponent(item?.template);
|
|
62
64
|
}, [item?.template]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { memo } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { StyleSheet, View } from 'react-native';
|
|
3
3
|
import { IconOutline } from '@ant-design/icons-react-native';
|
|
4
4
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
5
5
|
import { Colors } from '../../configs';
|
|
@@ -49,7 +49,6 @@ const styles = StyleSheet.create({
|
|
|
49
49
|
flexDirection: 'column',
|
|
50
50
|
justifyContent: 'center',
|
|
51
51
|
alignItems: 'center',
|
|
52
|
-
marginTop: Platform.OS === 'ios' ? 25 : 0,
|
|
53
52
|
},
|
|
54
53
|
connectStatus: {
|
|
55
54
|
flexDirection: 'row',
|
|
@@ -20,8 +20,8 @@ const SliderRangeActionTemplate = ({ device, item, onSelectAction }) => {
|
|
|
20
20
|
const [configValues] = useConfigGlobalState('configValues');
|
|
21
21
|
const [value, setValue] = useState();
|
|
22
22
|
|
|
23
|
-
const onInputChange = (
|
|
24
|
-
setValue(
|
|
23
|
+
const onInputChange = (e) => {
|
|
24
|
+
setValue(e);
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
useEffect(() => {
|
|
@@ -30,7 +30,7 @@ const SliderRangeActionTemplate = ({ device, item, onSelectAction }) => {
|
|
|
30
30
|
}, [configValues, config]);
|
|
31
31
|
|
|
32
32
|
const onPressDone = useCallback(() => {
|
|
33
|
-
let actionData = { value: value };
|
|
33
|
+
let actionData = { value: parseFloat(value) };
|
|
34
34
|
setValue(value);
|
|
35
35
|
onSelectAction &&
|
|
36
36
|
onSelectAction({
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React, { memo, useState, useCallback } from 'react';
|
|
2
|
+
import { View, TouchableOpacity } from 'react-native';
|
|
3
|
+
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
4
|
+
import styles from './TextBoxActionTemplateStyles';
|
|
5
|
+
import { Colors } from '../../configs';
|
|
6
|
+
import SelectActionCard from '../SelectActionCard';
|
|
7
|
+
import Text from '../Text';
|
|
8
|
+
import { AccessibilityLabel } from '../../configs/Constants';
|
|
9
|
+
import { ModalCustom } from '../Modal';
|
|
10
|
+
import _TextInput from '../Form/TextInput';
|
|
11
|
+
|
|
12
|
+
const TerminalBoxActionTemplate = ({ device, item, onSelectAction }) => {
|
|
13
|
+
const t = useTranslations();
|
|
14
|
+
const [visible, setVisible] = useState(false);
|
|
15
|
+
const onClose = () => setVisible(false);
|
|
16
|
+
const onPress = () => setVisible(true);
|
|
17
|
+
const { configuration, template, title } = item;
|
|
18
|
+
const { action } = configuration;
|
|
19
|
+
const [value, setValue] = useState();
|
|
20
|
+
const onInputChange = (e) => {
|
|
21
|
+
setValue(e);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const onPressDone = useCallback(() => {
|
|
25
|
+
let actionData = { value: value };
|
|
26
|
+
setValue(value);
|
|
27
|
+
onSelectAction &&
|
|
28
|
+
onSelectAction({
|
|
29
|
+
index: item.index,
|
|
30
|
+
action,
|
|
31
|
+
data: actionData,
|
|
32
|
+
template,
|
|
33
|
+
});
|
|
34
|
+
onClose();
|
|
35
|
+
}, [value, onSelectAction, item.index, action, template]);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<>
|
|
39
|
+
<SelectActionCard onPress={onPress} action={value} title={title} />
|
|
40
|
+
<ModalCustom
|
|
41
|
+
isVisible={visible}
|
|
42
|
+
onBackButtonPress={onClose}
|
|
43
|
+
onBackdropPress={onClose}
|
|
44
|
+
>
|
|
45
|
+
<View style={styles.popoverStyle}>
|
|
46
|
+
<View>
|
|
47
|
+
<Text type="H4" bold style={styles.textWithLine}>
|
|
48
|
+
{t('enter_parameters')}
|
|
49
|
+
</Text>
|
|
50
|
+
<View style={styles.modalContent}>
|
|
51
|
+
<_TextInput
|
|
52
|
+
wrapStyle={styles.wrapInputStyle}
|
|
53
|
+
value={value}
|
|
54
|
+
onChange={onInputChange}
|
|
55
|
+
/>
|
|
56
|
+
</View>
|
|
57
|
+
<View style={styles.wrapButton}>
|
|
58
|
+
<TouchableOpacity
|
|
59
|
+
onPress={onClose}
|
|
60
|
+
accessibilityLabel={AccessibilityLabel.TEXT_BOX_BUTTON_CANCEL}
|
|
61
|
+
>
|
|
62
|
+
<Text type="H4" bold color={Colors.Primary}>
|
|
63
|
+
{t('cancel')}
|
|
64
|
+
</Text>
|
|
65
|
+
</TouchableOpacity>
|
|
66
|
+
<TouchableOpacity
|
|
67
|
+
onPress={onPressDone}
|
|
68
|
+
accessibilityLabel={AccessibilityLabel.TEXT_BOX_BUTTON_DONE}
|
|
69
|
+
disabled={!value}
|
|
70
|
+
>
|
|
71
|
+
<Text
|
|
72
|
+
type="H4"
|
|
73
|
+
bold
|
|
74
|
+
color={!!value ? Colors.Primary : Colors.Gray}
|
|
75
|
+
>
|
|
76
|
+
{t('done')}
|
|
77
|
+
</Text>
|
|
78
|
+
</TouchableOpacity>
|
|
79
|
+
</View>
|
|
80
|
+
</View>
|
|
81
|
+
</View>
|
|
82
|
+
</ModalCustom>
|
|
83
|
+
</>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default memo(TerminalBoxActionTemplate);
|
|
@@ -20,8 +20,8 @@ const TextBoxActionTemplate = ({ device, item, onSelectAction }) => {
|
|
|
20
20
|
const [configValues] = useConfigGlobalState('configValues');
|
|
21
21
|
const [value, setValue] = useState();
|
|
22
22
|
|
|
23
|
-
const onInputChange = (
|
|
24
|
-
setValue(
|
|
23
|
+
const onInputChange = (e) => {
|
|
24
|
+
setValue(e);
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
useEffect(() => {
|
|
@@ -64,7 +64,7 @@ describe('Test SliderRangeActionTemplate', () => {
|
|
|
64
64
|
});
|
|
65
65
|
expect(mockOnSelectAction).toHaveBeenCalledWith({
|
|
66
66
|
action: 'b498234c-6c1a-452d-a1d1-87a314c20528',
|
|
67
|
-
data: { value:
|
|
67
|
+
data: { value: 123 },
|
|
68
68
|
index: undefined,
|
|
69
69
|
template: 'slider_range_template',
|
|
70
70
|
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { create, act } from 'react-test-renderer';
|
|
3
|
+
import { SCProvider } from '../../../context';
|
|
4
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
5
|
+
import { TouchableOpacity } from 'react-native';
|
|
6
|
+
import { AccessibilityLabel } from '../../../configs/Constants';
|
|
7
|
+
import SelectActionCard from '../../SelectActionCard';
|
|
8
|
+
import _TextInput from '../../Form/TextInput';
|
|
9
|
+
import TerminalBoxActionTemplate from '../TerminalBoxActionTemplate';
|
|
10
|
+
|
|
11
|
+
const mockOnSelectAction = jest.fn();
|
|
12
|
+
|
|
13
|
+
const wrapComponent = (item, params = {}) => (
|
|
14
|
+
<SCProvider initState={mockSCStore({})}>
|
|
15
|
+
<TerminalBoxActionTemplate
|
|
16
|
+
item={item}
|
|
17
|
+
onSelectAction={mockOnSelectAction}
|
|
18
|
+
{...params}
|
|
19
|
+
/>
|
|
20
|
+
</SCProvider>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
describe('Test TerminalBoxActionTemplate', () => {
|
|
24
|
+
let tree;
|
|
25
|
+
let data = {
|
|
26
|
+
title: '',
|
|
27
|
+
template: 'TerminalBoxTemplate',
|
|
28
|
+
configuration: {
|
|
29
|
+
action_data: {
|
|
30
|
+
id: 33283,
|
|
31
|
+
key: 'e8d1de74-7609-4e04-b63d-e6a19389d608',
|
|
32
|
+
icon: '',
|
|
33
|
+
name: 'String',
|
|
34
|
+
color: '#00979D',
|
|
35
|
+
},
|
|
36
|
+
from_config: {
|
|
37
|
+
id: 35490,
|
|
38
|
+
data: [],
|
|
39
|
+
unit: '',
|
|
40
|
+
color: '#000000',
|
|
41
|
+
chip_id: 4129,
|
|
42
|
+
},
|
|
43
|
+
to_config: {
|
|
44
|
+
id: 35491,
|
|
45
|
+
data: [],
|
|
46
|
+
unit: '',
|
|
47
|
+
color: '#000000',
|
|
48
|
+
chip_id: 4129,
|
|
49
|
+
},
|
|
50
|
+
action: 'b498234c-6c1a-452d-a1d1-87a314c20528',
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
mockOnSelectAction.mockClear();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const renderOptions = async (params = {}) => {
|
|
59
|
+
await act(async () => {
|
|
60
|
+
tree = await create(wrapComponent(data, params));
|
|
61
|
+
});
|
|
62
|
+
const instance = tree.root;
|
|
63
|
+
const card = instance.findByType(SelectActionCard);
|
|
64
|
+
await act(async () => {
|
|
65
|
+
card.props.onPress();
|
|
66
|
+
});
|
|
67
|
+
return instance;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
it('Test onPress Done', async () => {
|
|
71
|
+
const instance = await renderOptions();
|
|
72
|
+
const textInput = instance.findByType(_TextInput);
|
|
73
|
+
await act(async () => {
|
|
74
|
+
await textInput.props.onChange('123');
|
|
75
|
+
});
|
|
76
|
+
const touchableOpacity = instance.find(
|
|
77
|
+
(item) =>
|
|
78
|
+
item.props.accessibilityLabel ===
|
|
79
|
+
AccessibilityLabel.TEXT_BOX_BUTTON_DONE &&
|
|
80
|
+
item.type === TouchableOpacity
|
|
81
|
+
);
|
|
82
|
+
await act(async () => {
|
|
83
|
+
touchableOpacity.props.onPress();
|
|
84
|
+
});
|
|
85
|
+
expect(mockOnSelectAction).toHaveBeenCalledWith({
|
|
86
|
+
action: 'b498234c-6c1a-452d-a1d1-87a314c20528',
|
|
87
|
+
data: { value: '123' },
|
|
88
|
+
index: undefined,
|
|
89
|
+
template: 'TerminalBoxTemplate',
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('Test onPress Cancel', async () => {
|
|
94
|
+
const instance = await renderOptions();
|
|
95
|
+
const textInput = instance.findByType(_TextInput);
|
|
96
|
+
await act(async () => {
|
|
97
|
+
await textInput.props.onChange('');
|
|
98
|
+
});
|
|
99
|
+
const touchableDone = instance.find(
|
|
100
|
+
(item) =>
|
|
101
|
+
item.props.accessibilityLabel ===
|
|
102
|
+
AccessibilityLabel.TEXT_BOX_BUTTON_DONE &&
|
|
103
|
+
item.type === TouchableOpacity
|
|
104
|
+
);
|
|
105
|
+
expect(touchableDone.props.disabled).toBeTruthy();
|
|
106
|
+
|
|
107
|
+
const touchableOpacity = instance.find(
|
|
108
|
+
(item) =>
|
|
109
|
+
item.props.accessibilityLabel ===
|
|
110
|
+
AccessibilityLabel.TEXT_BOX_BUTTON_CANCEL &&
|
|
111
|
+
item.type === TouchableOpacity
|
|
112
|
+
);
|
|
113
|
+
await act(async () => {
|
|
114
|
+
touchableOpacity.props.onPress();
|
|
115
|
+
});
|
|
116
|
+
expect(mockOnSelectAction).not.toHaveBeenCalled();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -17,7 +17,11 @@ const fetchDataS3 = async (url) => {
|
|
|
17
17
|
return data;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
export const useFetchConfigHistory = (
|
|
20
|
+
export const useFetchConfigHistory = (
|
|
21
|
+
configs,
|
|
22
|
+
setChartData,
|
|
23
|
+
endpoint = API.CONFIG.DISPLAY_HISTORY_V3()
|
|
24
|
+
) => {
|
|
21
25
|
const fetchDataDisplayHistory = useCallback(
|
|
22
26
|
async (startDate, endDate) => {
|
|
23
27
|
if (!configs.length || !startDate || !endDate) {
|
|
@@ -33,24 +37,19 @@ export const useFetchConfigHistory = (configs, setChartData) => {
|
|
|
33
37
|
params.append('configs', item.id);
|
|
34
38
|
});
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
);
|
|
40
|
-
params.append(
|
|
41
|
-
'date_to',
|
|
42
|
-
endDate.subtract(7, 'hours').format('YYYY-MM-DDTHH:mm:ss')
|
|
43
|
-
);
|
|
40
|
+
const timezone = moment().utcOffset();
|
|
41
|
+
startDate = startDate.subtract(timezone, 'minutes');
|
|
42
|
+
endDate = endDate.subtract(timezone, 'minutes');
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
);
|
|
44
|
+
params.append('date_from', startDate.format('YYYY-MM-DDTHH:mm:ss'));
|
|
45
|
+
params.append('date_to', endDate.format('YYYY-MM-DDTHH:mm:ss'));
|
|
46
|
+
|
|
47
|
+
const { success, data } = await axiosGet(endpoint, {
|
|
48
|
+
params,
|
|
49
|
+
});
|
|
51
50
|
await updateConfigChart(success, data, configs, setChartData);
|
|
52
51
|
},
|
|
53
|
-
[configs, setChartData]
|
|
52
|
+
[configs, endpoint, setChartData]
|
|
54
53
|
);
|
|
55
54
|
return fetchDataDisplayHistory;
|
|
56
55
|
};
|
package/src/configs/API.js
CHANGED
|
@@ -83,6 +83,7 @@ const API = {
|
|
|
83
83
|
},
|
|
84
84
|
CONFIG: {
|
|
85
85
|
DISPLAY_HISTORY_V3: () => '/chip_manager/configs/value_history_v3/',
|
|
86
|
+
DISPLAY_HISTORY_V4: () => '/chip_manager/configs/value_history_v4/',
|
|
86
87
|
},
|
|
87
88
|
AUTOMATE: {
|
|
88
89
|
ACTION_ONE_TAP: (id) => `/property_manager/automate/${id}/action_one_tap/`,
|
|
@@ -564,6 +564,8 @@ export default {
|
|
|
564
564
|
TEXT_BOX_BUTTON_EDIT: 'TEXT_BOX_BUTTON_EDIT',
|
|
565
565
|
TEXT_BOX_BUTTON_CANCEL: 'TEXT_BOX_BUTTON_CANCEL',
|
|
566
566
|
TEXT_BOX_BUTTON_DONE: 'TEXT_BOX_BUTTON_DONE',
|
|
567
|
+
//TerminalBoxTemplate
|
|
568
|
+
TERMINAL_BOX_BUTTON_SEND: 'TERMINAL_BOX_BUTTON_SEND',
|
|
567
569
|
// OnOffButtonAction
|
|
568
570
|
ON_OFF_BUTTON_ACTION_TITLE: 'ON_OFF_BUTTON_ACTION_TITLE',
|
|
569
571
|
|
package/src/configs/Colors.js
CHANGED
|
@@ -15,6 +15,7 @@ import moment from 'moment';
|
|
|
15
15
|
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
16
16
|
import TextBoxActionTemplate from '../../../commons/OneTapTemplate/TextBoxActionTemplate';
|
|
17
17
|
import SliderRangeActionTemplate from '../../../commons/OneTapTemplate/SliderRangeActionTemplate';
|
|
18
|
+
import TerminalBoxActionTemplate from '../../../commons/OneTapTemplate/TerminalBoxActionTemplate';
|
|
18
19
|
|
|
19
20
|
const RenderActionItem = ({ device, item, handleOnSelectAction, index, t }) => {
|
|
20
21
|
item.index = index;
|
|
@@ -78,6 +79,15 @@ const RenderActionItem = ({ device, item, handleOnSelectAction, index, t }) => {
|
|
|
78
79
|
onSelectAction={handleOnSelectAction}
|
|
79
80
|
/>
|
|
80
81
|
);
|
|
82
|
+
case 'TerminalBoxTemplate':
|
|
83
|
+
return (
|
|
84
|
+
<TerminalBoxActionTemplate
|
|
85
|
+
key={item.id}
|
|
86
|
+
device={device}
|
|
87
|
+
item={item}
|
|
88
|
+
onSelectAction={handleOnSelectAction}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
81
91
|
default:
|
|
82
92
|
ToastBottomHelper.error(
|
|
83
93
|
t('template_not_supported', { template: item.template }),
|
|
@@ -324,6 +324,7 @@ const Item = ({ item, index, enableScript, t }) => {
|
|
|
324
324
|
station_name,
|
|
325
325
|
sensor_name,
|
|
326
326
|
action_name,
|
|
327
|
+
data,
|
|
327
328
|
} = action_script;
|
|
328
329
|
return (
|
|
329
330
|
<View style={styles.wrapItem}>
|
|
@@ -358,7 +359,7 @@ const Item = ({ item, index, enableScript, t }) => {
|
|
|
358
359
|
{sensor_name}
|
|
359
360
|
</Text>
|
|
360
361
|
<Text numberOfLines={1} type="H4" color={color}>
|
|
361
|
-
{action_name}
|
|
362
|
+
{action_name} {data}
|
|
362
363
|
</Text>
|
|
363
364
|
</View>
|
|
364
365
|
</View>
|
|
@@ -171,6 +171,7 @@ export const SensorDisplayItem = ({
|
|
|
171
171
|
case 'two_button_action_template':
|
|
172
172
|
case 'switch_button_action_template':
|
|
173
173
|
case 'TextBoxTemplate':
|
|
174
|
+
case 'TerminalBoxTemplate':
|
|
174
175
|
return (
|
|
175
176
|
<ActionGroup
|
|
176
177
|
accessibilityLabel={AccessibilityLabel.DEVICE_DETAIL_ACTION_GROUP}
|
|
@@ -500,29 +500,34 @@ const DeviceDetail = ({ route }) => {
|
|
|
500
500
|
useFocusEffect(
|
|
501
501
|
useCallback(() => {
|
|
502
502
|
let params = new URLSearchParams();
|
|
503
|
-
let
|
|
504
|
-
|
|
505
|
-
display.items.
|
|
506
|
-
|
|
503
|
+
let configIdsSet = new Set();
|
|
504
|
+
|
|
505
|
+
display.items.forEach((item) => {
|
|
506
|
+
const { configuration } = item;
|
|
507
|
+
|
|
508
|
+
if (!configuration) {
|
|
507
509
|
return;
|
|
508
510
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
511
|
+
|
|
512
|
+
const { configs, options, config, from_config, to_config } =
|
|
513
|
+
configuration;
|
|
514
|
+
|
|
515
|
+
configs?.forEach((config) => configIdsSet.add(config.id));
|
|
516
|
+
options?.forEach((option) => configIdsSet.add(option.config));
|
|
517
|
+
|
|
518
|
+
if (config) {
|
|
519
|
+
const configId = config.id || config;
|
|
520
|
+
configIdsSet.add(configId);
|
|
521
|
+
}
|
|
522
|
+
if (from_config) {
|
|
523
|
+
configIdsSet.add(from_config.id);
|
|
524
|
+
}
|
|
525
|
+
if (to_config) {
|
|
526
|
+
configIdsSet.add(to_config.id);
|
|
523
527
|
}
|
|
524
528
|
});
|
|
525
529
|
|
|
530
|
+
let configIds = Array.from(configIdsSet);
|
|
526
531
|
configIds = configIds.filter(Boolean);
|
|
527
532
|
configIdsTemp.current = configIds;
|
|
528
533
|
|
|
@@ -1467,7 +1467,7 @@ export default {
|
|
|
1467
1467
|
'customize...': 'Customize...',
|
|
1468
1468
|
uri_invalid: 'URI invalid',
|
|
1469
1469
|
when_value_is: 'When value is',
|
|
1470
|
-
template_not_supported: '"{template}" not yet supported',
|
|
1470
|
+
template_not_supported: 'Widget "{template}" not yet supported',
|
|
1471
1471
|
invited_user: 'Invited user {user}',
|
|
1472
1472
|
enter_value: 'Enter value',
|
|
1473
1473
|
enter_parameters: 'Enter parameters',
|
|
@@ -1477,7 +1477,7 @@ export default {
|
|
|
1477
1477
|
'customize...': 'Tùy chỉnh...',
|
|
1478
1478
|
uri_invalid: 'URI không hợp lệ',
|
|
1479
1479
|
when_value_is: 'Khi giá trị là',
|
|
1480
|
-
template_not_supported: '"{template}" chưa được hỗ trợ',
|
|
1480
|
+
template_not_supported: 'Tiện ích "{template}" chưa được hỗ trợ',
|
|
1481
1481
|
invited_user: 'Đã mời người dùng {user}',
|
|
1482
1482
|
enter_value: 'Nhập giá trị',
|
|
1483
1483
|
enter_parameters: 'Nhập thông số',
|