@eohjsc/react-native-smart-city 0.2.68 → 0.2.69
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/CurtainButtonTemplate.js +0 -30
- package/src/commons/ActionGroup/CurtainButtonTemplateStyle.js +0 -12
- package/src/commons/ActionGroup/__test__/CurtainButtonTemplate.test.js +1 -1
- package/src/commons/AlertAction/index.js +4 -3
- package/src/commons/SubUnit/OneTap/OneTapStyles.js +14 -5
- package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +5 -68
- package/src/commons/SubUnit/OneTap/index.js +24 -33
- package/src/commons/ViewButtonBottom/index.js +4 -0
- package/src/screens/SharedUnit/index.js +7 -5
- package/src/screens/Sharing/Components/SensorItem.js +27 -12
- package/src/screens/Sharing/Components/Styles/SensorItemStyles.js +4 -0
- package/src/screens/Sharing/SelectPermission.js +44 -32
- package/src/screens/Sharing/__test__/SelectPermission.test.js +2 -1
- package/src/screens/SubUnit/AddSubUnit.js +4 -1
- package/src/screens/Unit/Detail.js +2 -2
- package/src/screens/Unit/SmartAccount.js +36 -15
- package/src/screens/Unit/SmartAccountStyles.js +20 -0
- package/src/screens/Unit/hook/useStateAlertRemove.js +4 -6
- package/src/utils/I18n/translations/en.json +3 -2
- package/src/utils/I18n/translations/vi.json +3 -2
- package/src/commons/ActionGroup/__test__/MenuActionAddSchedule.test.js +0 -71
- package/src/commons/ActionGroup/hooks/AccessScheduleDetailStyles.js +0 -41
- package/src/commons/ActionGroup/hooks/MenuActionAddSchedule.js +0 -110
- package/src/commons/ActionGroup/hooks/MenuActionAddScheduleStyle.js +0 -69
- package/src/commons/ActionGroup/hooks/RecurringDetail.js +0 -97
package/package.json
CHANGED
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
import React, { memo, useCallback, useState } from 'react';
|
|
2
2
|
import { TouchableOpacity, View, Switch, Image } from 'react-native';
|
|
3
3
|
import { Icon } from '@ant-design/react-native';
|
|
4
|
-
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
5
4
|
import styles from './CurtainButtonTemplateStyle';
|
|
6
5
|
import Text from '../Text';
|
|
7
6
|
import { TESTID } from '../../configs/Constants';
|
|
8
7
|
import { Colors, Images } from '../../configs';
|
|
9
|
-
import { Section } from '../../commons/Section';
|
|
10
|
-
import { IconOutline } from '@ant-design/icons-react-native';
|
|
11
|
-
import MenuActionAddSchedule from './hooks/MenuActionAddSchedule';
|
|
12
|
-
import { useBoolean } from '../../hooks/Common';
|
|
13
8
|
|
|
14
9
|
const CurtainButtonTemplate = memo(({ actionGroup, doAction }) => {
|
|
15
|
-
const t = useTranslations();
|
|
16
10
|
const { configuration } = actionGroup;
|
|
17
11
|
const [lock, setLock] = useState(false);
|
|
18
|
-
const [isShowModal, setShowModal, setHideModal] = useBoolean(false);
|
|
19
|
-
|
|
20
12
|
const onButtonOpenPress = useCallback(
|
|
21
13
|
() => doAction(configuration.open_action_data),
|
|
22
14
|
[configuration.open_action_data, doAction]
|
|
@@ -118,28 +110,6 @@ const CurtainButtonTemplate = memo(({ actionGroup, doAction }) => {
|
|
|
118
110
|
lock ? Colors.Primary : Colors.Gray6
|
|
119
111
|
)}
|
|
120
112
|
</View>
|
|
121
|
-
<Section style={styles.section}>
|
|
122
|
-
<View style={styles.reminderOption}>
|
|
123
|
-
<Text type="H4" color={Colors.Gray8}>
|
|
124
|
-
{t('schedule')}
|
|
125
|
-
</Text>
|
|
126
|
-
<TouchableOpacity onPress={setShowModal}>
|
|
127
|
-
<IconOutline name="plus" size={20} />
|
|
128
|
-
</TouchableOpacity>
|
|
129
|
-
</View>
|
|
130
|
-
<View>
|
|
131
|
-
<TouchableOpacity
|
|
132
|
-
onPress={setShowModal}
|
|
133
|
-
style={styles.tapToAddSchedule}
|
|
134
|
-
>
|
|
135
|
-
<Text type="Body" color={Colors.Gray6} center>
|
|
136
|
-
{t('tap_to_add_new_schedule')}
|
|
137
|
-
</Text>
|
|
138
|
-
</TouchableOpacity>
|
|
139
|
-
</View>
|
|
140
|
-
|
|
141
|
-
<MenuActionAddSchedule visible={isShowModal} hideModal={setHideModal} />
|
|
142
|
-
</Section>
|
|
143
113
|
</>
|
|
144
114
|
);
|
|
145
115
|
});
|
|
@@ -29,18 +29,6 @@ export default StyleSheet.create({
|
|
|
29
29
|
alignItems: 'flex-end',
|
|
30
30
|
paddingLeft: 5,
|
|
31
31
|
},
|
|
32
|
-
section: {
|
|
33
|
-
marginHorizontal: 20,
|
|
34
|
-
borderRadius: 10,
|
|
35
|
-
},
|
|
36
|
-
reminderOption: {
|
|
37
|
-
flex: 1,
|
|
38
|
-
flexDirection: 'row',
|
|
39
|
-
justifyContent: 'space-between',
|
|
40
|
-
},
|
|
41
|
-
tapToAddSchedule: {
|
|
42
|
-
paddingVertical: 50,
|
|
43
|
-
},
|
|
44
32
|
buttonActionCurtain: {
|
|
45
33
|
flex: 1,
|
|
46
34
|
aspectRatio: 1,
|
|
@@ -67,6 +67,6 @@ describe('Test CurtainButtonTemplate', () => {
|
|
|
67
67
|
});
|
|
68
68
|
const instance = wrapper.root;
|
|
69
69
|
const touchableOpacities = instance.findAllByType(TouchableOpacity);
|
|
70
|
-
expect(touchableOpacities.length).toEqual(
|
|
70
|
+
expect(touchableOpacities.length).toEqual(3);
|
|
71
71
|
});
|
|
72
72
|
});
|
|
@@ -22,6 +22,8 @@ const AlertAction = ({
|
|
|
22
22
|
children,
|
|
23
23
|
testIDPrefix = '',
|
|
24
24
|
animatedStyle,
|
|
25
|
+
boxLeftButtonStyle,
|
|
26
|
+
boxRightButtonStyle,
|
|
25
27
|
}) => {
|
|
26
28
|
return (
|
|
27
29
|
<ModalCustom
|
|
@@ -54,6 +56,8 @@ const AlertAction = ({
|
|
|
54
56
|
onRightClick={rightButtonClick}
|
|
55
57
|
styleButtonLeftText={leftButtonStyle}
|
|
56
58
|
styleButtonRightText={rightButtonStyle}
|
|
59
|
+
styleButtonLeft={boxLeftButtonStyle}
|
|
60
|
+
styleButtonRight={boxRightButtonStyle}
|
|
57
61
|
testIDPrefix={testIDPrefix}
|
|
58
62
|
/>
|
|
59
63
|
</View>
|
|
@@ -85,10 +89,8 @@ const styles = StyleSheet.create({
|
|
|
85
89
|
modalHeader: {
|
|
86
90
|
padding: 16,
|
|
87
91
|
backgroundColor: Colors.White,
|
|
88
|
-
borderBottomWidth: 1,
|
|
89
92
|
borderTopLeftRadius: 20,
|
|
90
93
|
borderTopRightRadius: 20,
|
|
91
|
-
borderColor: Colors.Gray4,
|
|
92
94
|
},
|
|
93
95
|
modalHeaderText: {
|
|
94
96
|
fontSize: 16,
|
|
@@ -97,7 +99,6 @@ const styles = StyleSheet.create({
|
|
|
97
99
|
},
|
|
98
100
|
descriptionText: {
|
|
99
101
|
paddingHorizontal: 16,
|
|
100
|
-
paddingTop: 16,
|
|
101
102
|
},
|
|
102
103
|
});
|
|
103
104
|
|
|
@@ -33,13 +33,19 @@ export default StyleSheet.create({
|
|
|
33
33
|
alignItems: 'center',
|
|
34
34
|
paddingBottom: 3,
|
|
35
35
|
},
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
borderSelection: {
|
|
37
|
+
flexDirection: 'row',
|
|
38
|
+
justifyContent: 'center',
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
paddingHorizontal: 5,
|
|
41
|
+
paddingLeft: 15,
|
|
39
42
|
borderRadius: 30,
|
|
40
43
|
backgroundColor: Colors.Gray3,
|
|
41
44
|
},
|
|
42
|
-
|
|
45
|
+
notShowBorder: {
|
|
46
|
+
paddingLeft: 15,
|
|
47
|
+
},
|
|
48
|
+
boxTextScenarito: {
|
|
43
49
|
justifyContent: 'center',
|
|
44
50
|
flexDirection: 'row',
|
|
45
51
|
alignItems: 'center',
|
|
@@ -48,8 +54,11 @@ export default StyleSheet.create({
|
|
|
48
54
|
android: 6,
|
|
49
55
|
}),
|
|
50
56
|
},
|
|
51
|
-
|
|
57
|
+
textSelection: {
|
|
52
58
|
color: Colors.Primary,
|
|
53
59
|
marginRight: 10,
|
|
54
60
|
},
|
|
61
|
+
boxScenario: {
|
|
62
|
+
flexDirection: 'row',
|
|
63
|
+
},
|
|
55
64
|
});
|
|
@@ -267,82 +267,19 @@ describe('test Item', () => {
|
|
|
267
267
|
expect(goDetail).toHaveLength(1);
|
|
268
268
|
});
|
|
269
269
|
|
|
270
|
-
test('render click select option
|
|
271
|
-
data.isOwner = false;
|
|
272
|
-
data.type = 'schedule';
|
|
273
|
-
data.listAutomate = [
|
|
274
|
-
{
|
|
275
|
-
text: AUTOMATE_TYPE.SCENARIO,
|
|
276
|
-
type: AUTOMATE_TYPE.ONE_TAP,
|
|
277
|
-
data: [
|
|
278
|
-
{
|
|
279
|
-
id: 1,
|
|
280
|
-
user: 6,
|
|
281
|
-
type: 'one_tap',
|
|
282
|
-
activate_at: null,
|
|
283
|
-
script: {
|
|
284
|
-
name: 'One-tap',
|
|
285
|
-
icon: '',
|
|
286
|
-
icon_kit: '',
|
|
287
|
-
},
|
|
288
|
-
},
|
|
289
|
-
],
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
text: AUTOMATE_TYPE.AUTOMATION,
|
|
293
|
-
type: AUTOMATE_TYPE.AUTOMATION,
|
|
294
|
-
data: [
|
|
295
|
-
{
|
|
296
|
-
id: 2,
|
|
297
|
-
user: 6,
|
|
298
|
-
type: 'schedule',
|
|
299
|
-
activate_at: null,
|
|
300
|
-
script: {
|
|
301
|
-
name: 'Rain',
|
|
302
|
-
icon: '',
|
|
303
|
-
icon_kit: '',
|
|
304
|
-
},
|
|
305
|
-
},
|
|
306
|
-
],
|
|
307
|
-
},
|
|
308
|
-
];
|
|
309
|
-
const response = {
|
|
310
|
-
status: 200,
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
await axios.post.mockImplementation(async () => {
|
|
314
|
-
return response;
|
|
315
|
-
});
|
|
316
|
-
|
|
270
|
+
test('render click select option by automation', async () => {
|
|
317
271
|
await act(async () => {
|
|
318
272
|
tree = await create(wrapComponent(data));
|
|
319
273
|
});
|
|
320
|
-
|
|
321
274
|
const instance = tree.root;
|
|
322
|
-
const
|
|
275
|
+
const touchableOpacity = instance.findAll(
|
|
323
276
|
(el) =>
|
|
324
277
|
el.props.testID === TESTID.SUB_UNIT_SELECT_AUTOMATE_TYPE &&
|
|
325
278
|
el.type === TouchableOpacity
|
|
326
279
|
);
|
|
327
|
-
|
|
328
|
-
|
|
280
|
+
act(() => {
|
|
281
|
+
touchableOpacity[0].props.onPress();
|
|
329
282
|
});
|
|
330
|
-
|
|
331
|
-
const menu = instance.find(
|
|
332
|
-
(el) => el.props.testID === TESTID.NAVBAR_MENU_ACTION_MORE
|
|
333
|
-
);
|
|
334
|
-
|
|
335
|
-
expect(menu.props.isVisible).toBeTruthy();
|
|
336
|
-
|
|
337
|
-
const automation = menu.props.listMenuItem[1];
|
|
338
|
-
|
|
339
|
-
await act(async () => {
|
|
340
|
-
await menu.props.onItemClick(automation, 1);
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
const text = instance.find(
|
|
344
|
-
(el) => el.props.testID === TESTID.SUB_UNIT_TEXT_DROPDOWN
|
|
345
|
-
);
|
|
346
|
-
expect(text.props.children).toEqual(AUTOMATE_TYPE.AUTOMATION);
|
|
283
|
+
expect(touchableOpacity).toHaveLength(1);
|
|
347
284
|
});
|
|
348
285
|
});
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { TouchableOpacity, View } from 'react-native';
|
|
3
|
-
import { Icon } from '@ant-design/react-native';
|
|
4
3
|
|
|
5
|
-
import {
|
|
4
|
+
import { Section } from '../..';
|
|
6
5
|
import ItemAddNew from '../../Device/ItemAddNew';
|
|
7
6
|
import ItemOneTap from './ItemOneTap';
|
|
8
7
|
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
9
8
|
import { useNavigation } from '@react-navigation/native';
|
|
10
9
|
import Routes from '../../../utils/Route/index.js';
|
|
11
10
|
import { AUTOMATE_TYPE, TESTID } from '../../../configs/Constants';
|
|
12
|
-
import { Colors } from '../../../configs/Colors.js';
|
|
13
|
-
import usePopover from '../../../hooks/Common/usePopover.js';
|
|
14
11
|
import Text from '../../Text/index.js';
|
|
15
12
|
|
|
16
13
|
import styles from './OneTapStyles.js';
|
|
@@ -33,11 +30,8 @@ const SubUnitAutomate = ({ isOwner, listAutomate, unit, wrapItemStyle }) => {
|
|
|
33
30
|
break;
|
|
34
31
|
}
|
|
35
32
|
};
|
|
36
|
-
const { childRef, showingPopover, showPopoverWithRef, hidePopover } =
|
|
37
|
-
usePopover();
|
|
38
33
|
|
|
39
34
|
const refMenuAction = useRef();
|
|
40
|
-
const handleShowMenuAction = () => showPopoverWithRef(refMenuAction);
|
|
41
35
|
|
|
42
36
|
const onItemClick = useCallback((item, index) => {
|
|
43
37
|
setAutomates(item);
|
|
@@ -50,23 +44,30 @@ const SubUnitAutomate = ({ isOwner, listAutomate, unit, wrapItemStyle }) => {
|
|
|
50
44
|
|
|
51
45
|
return (
|
|
52
46
|
<Section style={styles.noShadow}>
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
47
|
+
<View style={styles.boxScenario}>
|
|
48
|
+
{listAutomate.map((item, index) => (
|
|
49
|
+
<TouchableOpacity
|
|
50
|
+
style={
|
|
51
|
+
indexAutomate === index
|
|
52
|
+
? styles.borderSelection
|
|
53
|
+
: styles.notShowBorder
|
|
54
|
+
}
|
|
55
|
+
onPress={() => onItemClick(item, index)}
|
|
56
|
+
ref={refMenuAction}
|
|
57
|
+
testID={TESTID.SUB_UNIT_SELECT_AUTOMATE_TYPE}
|
|
64
58
|
>
|
|
65
|
-
{
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
<View style={styles.boxTextScenarito}>
|
|
60
|
+
<Text
|
|
61
|
+
testID={TESTID.SUB_UNIT_TEXT_DROPDOWN}
|
|
62
|
+
style={styles.textSelection}
|
|
63
|
+
semibold
|
|
64
|
+
>
|
|
65
|
+
{item?.text}
|
|
66
|
+
</Text>
|
|
67
|
+
</View>
|
|
68
|
+
</TouchableOpacity>
|
|
69
|
+
))}
|
|
70
|
+
</View>
|
|
70
71
|
<View style={styles.boxDevices}>
|
|
71
72
|
{automates?.data &&
|
|
72
73
|
automates.data.map((item) => (
|
|
@@ -78,16 +79,6 @@ const SubUnitAutomate = ({ isOwner, listAutomate, unit, wrapItemStyle }) => {
|
|
|
78
79
|
wrapStyle={wrapItemStyle}
|
|
79
80
|
/>
|
|
80
81
|
</View>
|
|
81
|
-
<MenuActionMore
|
|
82
|
-
isVisible={showingPopover}
|
|
83
|
-
hideMore={hidePopover}
|
|
84
|
-
listMenuItem={listAutomate}
|
|
85
|
-
childRef={childRef}
|
|
86
|
-
onItemClick={onItemClick}
|
|
87
|
-
isTextCenter={false}
|
|
88
|
-
testID={TESTID.NAVBAR_MENU_ACTION_MORE}
|
|
89
|
-
wrapStyle={styles.wrapStyle}
|
|
90
|
-
/>
|
|
91
82
|
</Section>
|
|
92
83
|
);
|
|
93
84
|
};
|
|
@@ -16,6 +16,8 @@ const ViewButtonBottom = ({
|
|
|
16
16
|
styleButton,
|
|
17
17
|
styleButtonLeftText,
|
|
18
18
|
styleButtonRightText,
|
|
19
|
+
styleButtonLeft,
|
|
20
|
+
styleButtonRight,
|
|
19
21
|
testIDPrefix = '',
|
|
20
22
|
}) => {
|
|
21
23
|
const useTwoButton = leftTitle && rightTitle;
|
|
@@ -27,6 +29,7 @@ const ViewButtonBottom = ({
|
|
|
27
29
|
style={[
|
|
28
30
|
styles.button,
|
|
29
31
|
styleButton,
|
|
32
|
+
styleButtonLeft,
|
|
30
33
|
useTwoButton && styles.buttonMarginRight,
|
|
31
34
|
]}
|
|
32
35
|
onPress={onLeftClick}
|
|
@@ -48,6 +51,7 @@ const ViewButtonBottom = ({
|
|
|
48
51
|
style={[
|
|
49
52
|
styles.button,
|
|
50
53
|
styleButton,
|
|
54
|
+
styleButtonRight,
|
|
51
55
|
useTwoButton && styles.buttonMarginLeft,
|
|
52
56
|
]}
|
|
53
57
|
onPress={onRightClick}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
|
2
2
|
import { View, TouchableOpacity, FlatList, RefreshControl } from 'react-native';
|
|
3
|
-
import { useNavigation } from '@react-navigation/native';
|
|
3
|
+
import { useNavigation, useIsFocused } from '@react-navigation/native';
|
|
4
|
+
|
|
4
5
|
import { axiosGet } from '../../utils/Apis/axios';
|
|
5
6
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
6
|
-
|
|
7
7
|
import { API } from '../../configs';
|
|
8
8
|
import Text from '../../commons/Text';
|
|
9
9
|
import SharedUnit from '../../commons/Unit/SharedUnit';
|
|
@@ -24,7 +24,7 @@ const Shared = () => {
|
|
|
24
24
|
useTitleHeader(t('text_shared_with_me'));
|
|
25
25
|
const navigation = useNavigation();
|
|
26
26
|
const [tab, setTabActiveState] = useState(0);
|
|
27
|
-
|
|
27
|
+
const isFocused = useIsFocused();
|
|
28
28
|
const [sharedUnits, setSharedUnits] = useState([]);
|
|
29
29
|
|
|
30
30
|
const dataStarred = useMemo(
|
|
@@ -73,8 +73,10 @@ const Shared = () => {
|
|
|
73
73
|
}, [language, forceUpdate]);
|
|
74
74
|
|
|
75
75
|
useEffect(() => {
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
if (isFocused) {
|
|
77
|
+
filterSharedUnits(filter);
|
|
78
|
+
}
|
|
79
|
+
}, [filterSharedUnits, filter, isFocused]);
|
|
78
80
|
|
|
79
81
|
const onRefresh = useCallback(() => {
|
|
80
82
|
filterSharedUnits(filter);
|
|
@@ -5,11 +5,13 @@ import { Colors } from '../../../configs';
|
|
|
5
5
|
import styles from './Styles/SensorItemStyles';
|
|
6
6
|
import FImage from '../../../commons/FImage';
|
|
7
7
|
import { TitleCheckBox } from '.';
|
|
8
|
+
import { CheckBoxCustom } from '.';
|
|
8
9
|
|
|
9
10
|
const SensorItem = ({
|
|
10
11
|
item = {},
|
|
11
12
|
isRenderSeparated,
|
|
12
13
|
onTickedChild,
|
|
14
|
+
onTickedSensor,
|
|
13
15
|
titleGroup,
|
|
14
16
|
activeItemId,
|
|
15
17
|
setActiveItemId,
|
|
@@ -21,21 +23,22 @@ const SensorItem = ({
|
|
|
21
23
|
actions = [],
|
|
22
24
|
read_configs = [],
|
|
23
25
|
icon_kit = '',
|
|
26
|
+
isChecked,
|
|
24
27
|
} = item;
|
|
25
|
-
const [expanded, setExpanded] = useState(activeItemId === id);
|
|
26
28
|
const [dataConfig, setDataConfig] = useState([
|
|
27
29
|
...actions,
|
|
28
30
|
...read_configs.map((i) => ({ ...i, isConfig: true })),
|
|
29
31
|
]);
|
|
30
32
|
|
|
33
|
+
const expanded = activeItemId === id;
|
|
34
|
+
|
|
31
35
|
const onPressItem = () => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
if (dataConfig.length > 0) {
|
|
37
|
+
expanded ? setActiveItemId(-1) : setActiveItemId(id);
|
|
38
|
+
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
|
35
39
|
} else {
|
|
36
|
-
|
|
40
|
+
handleOnTickedSensor();
|
|
37
41
|
}
|
|
38
|
-
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
|
39
42
|
};
|
|
40
43
|
|
|
41
44
|
const handleOnTickedChild = (idGroup, isChecked, childId) => {
|
|
@@ -56,6 +59,10 @@ const SensorItem = ({
|
|
|
56
59
|
);
|
|
57
60
|
};
|
|
58
61
|
|
|
62
|
+
const handleOnTickedSensor = () => {
|
|
63
|
+
onTickedSensor && onTickedSensor(idGroup, id, !isChecked);
|
|
64
|
+
};
|
|
65
|
+
|
|
59
66
|
useEffect(() => {
|
|
60
67
|
if (
|
|
61
68
|
Platform.OS === 'android' &&
|
|
@@ -97,12 +104,20 @@ const SensorItem = ({
|
|
|
97
104
|
<Text numberOfLines={1} style={styles.text} onPress={onPressItem}>
|
|
98
105
|
{name}
|
|
99
106
|
</Text>
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
{dataConfig.length > 0 ? (
|
|
108
|
+
<IconOutline
|
|
109
|
+
onPress={onPressItem}
|
|
110
|
+
name={expanded ? 'up' : 'down'}
|
|
111
|
+
size={20}
|
|
112
|
+
color={Colors.Gray6}
|
|
113
|
+
/>
|
|
114
|
+
) : (
|
|
115
|
+
<CheckBoxCustom
|
|
116
|
+
isChecked={isChecked}
|
|
117
|
+
onPress={onPressItem}
|
|
118
|
+
wrapStyle={styles.checkBox}
|
|
119
|
+
/>
|
|
120
|
+
)}
|
|
106
121
|
</View>
|
|
107
122
|
{expanded && <View style={styles.wrapExpand}>{renderData}</View>}
|
|
108
123
|
{isRenderSeparated && <View style={styles.viewSeparated} />}
|
|
@@ -38,11 +38,12 @@ const SelectPermission = ({ route }) => {
|
|
|
38
38
|
...i,
|
|
39
39
|
isChecked,
|
|
40
40
|
}));
|
|
41
|
-
for (let
|
|
42
|
-
for (let item in data[
|
|
43
|
-
const itemTemp = data[
|
|
44
|
-
data[
|
|
41
|
+
for (let station in data) {
|
|
42
|
+
for (let item in data[station].sensors) {
|
|
43
|
+
const itemTemp = data[station].sensors[item];
|
|
44
|
+
data[station].sensors[item] = {
|
|
45
45
|
...itemTemp,
|
|
46
|
+
isChecked,
|
|
46
47
|
actions: itemTemp.actions.map((i) => ({
|
|
47
48
|
...i,
|
|
48
49
|
isChecked,
|
|
@@ -63,6 +64,7 @@ const SelectPermission = ({ route }) => {
|
|
|
63
64
|
isChecked,
|
|
64
65
|
sensors: data[index]?.sensors.map((i) => ({
|
|
65
66
|
...i,
|
|
67
|
+
isChecked,
|
|
66
68
|
actions: i.actions.map((j) => ({ ...j, isChecked })),
|
|
67
69
|
read_configs: i.read_configs.map((j) => ({ ...j, isChecked })),
|
|
68
70
|
})),
|
|
@@ -80,33 +82,38 @@ const SelectPermission = ({ route }) => {
|
|
|
80
82
|
isReadConfig
|
|
81
83
|
) => {
|
|
82
84
|
let data = [...dataStationTemp];
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
|
|
85
|
+
const group = data.find((i) => i.id === idGroup);
|
|
86
|
+
const sensor = group.sensors.find((i) => i.id === sensorId);
|
|
87
|
+
const child = sensor[`${isReadConfig ? 'read_configs' : 'actions'}`].find(
|
|
88
|
+
(i) => i.id === childId
|
|
86
89
|
);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
90
|
+
|
|
91
|
+
child.isChecked = isChecked;
|
|
92
|
+
sensor.isChecked = !(
|
|
93
|
+
sensor.read_configs.some((i) => !i.isChecked) ||
|
|
94
|
+
sensor.actions.some((i) => !i.isChecked)
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
for (let station of data) {
|
|
98
|
+
if (station.sensors.length) {
|
|
99
|
+
station.isChecked = !station.sensors.some((i) => !i.isChecked);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
setIsTickAllDevices(!dataStationTemp.some((i) => !i.isChecked));
|
|
103
|
+
dataStationTemp = data;
|
|
104
|
+
setDataStations(data);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const onTickedSensor = (idGroup, sensorId, isChecked) => {
|
|
108
|
+
let data = [...dataStationTemp];
|
|
109
|
+
const group = data.find((i) => i.id === idGroup);
|
|
110
|
+
const sensor = group.sensors.find((i) => i.id === sensorId);
|
|
111
|
+
|
|
112
|
+
sensor.isChecked = isChecked;
|
|
113
|
+
|
|
114
|
+
for (let station of data) {
|
|
115
|
+
if (station.sensors.length) {
|
|
116
|
+
station.isChecked = !station.sensors.some((i) => !i.isChecked);
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
setIsTickAllDevices(!dataStationTemp.some((i) => !i.isChecked));
|
|
@@ -133,6 +140,7 @@ const SelectPermission = ({ route }) => {
|
|
|
133
140
|
key={i.id}
|
|
134
141
|
isRenderSeparated={index !== sensors.length - 1}
|
|
135
142
|
onTickedChild={onTickedChild}
|
|
143
|
+
onTickedSensor={onTickedSensor}
|
|
136
144
|
titleGroup={name}
|
|
137
145
|
activeItemId={activeItemId}
|
|
138
146
|
setActiveItemId={setActiveItemId}
|
|
@@ -147,8 +155,8 @@ const SelectPermission = ({ route }) => {
|
|
|
147
155
|
const onPressNext = () => {
|
|
148
156
|
let read_permissions = [],
|
|
149
157
|
control_permissions = [];
|
|
150
|
-
for (let
|
|
151
|
-
for (let item of
|
|
158
|
+
for (let station of dataStationTemp) {
|
|
159
|
+
for (let item of station.sensors) {
|
|
152
160
|
let arrIdControlTemp = [],
|
|
153
161
|
arrIdReadTemp = [];
|
|
154
162
|
for (let i of item.actions) {
|
|
@@ -161,6 +169,10 @@ const SelectPermission = ({ route }) => {
|
|
|
161
169
|
control_permissions.push({ id: item.id, values: arrIdControlTemp });
|
|
162
170
|
arrIdReadTemp.length &&
|
|
163
171
|
read_permissions.push({ id: item.id, values: arrIdReadTemp });
|
|
172
|
+
!arrIdControlTemp.length &&
|
|
173
|
+
!arrIdReadTemp.length &&
|
|
174
|
+
item.isChecked &&
|
|
175
|
+
read_permissions.push({ id: item.id, values: [] });
|
|
164
176
|
}
|
|
165
177
|
}
|
|
166
178
|
if (!read_permissions.length && !control_permissions.length) {
|
|
@@ -144,6 +144,7 @@ describe('Test SelectPermission', () => {
|
|
|
144
144
|
{
|
|
145
145
|
actions: [{ id: 136, isChecked: true, name: 'action 1' }],
|
|
146
146
|
id: 123,
|
|
147
|
+
isChecked: true,
|
|
147
148
|
name: 'child1',
|
|
148
149
|
read_configs: [{ id: 137, isChecked: true, name: 'config 1' }],
|
|
149
150
|
},
|
|
@@ -165,7 +166,7 @@ describe('Test SelectPermission', () => {
|
|
|
165
166
|
const SensorItemElement = instance.findAllByType(SensorItem);
|
|
166
167
|
expect(SensorItemElement).toHaveLength(1);
|
|
167
168
|
act(() => {
|
|
168
|
-
SensorItemElement[0].props.onTickedChild(204, 123,
|
|
169
|
+
SensorItemElement[0].props.onTickedChild(204, 123, 137, true, true);
|
|
169
170
|
});
|
|
170
171
|
expect(mockSetDataStations).toBeCalled();
|
|
171
172
|
});
|
|
@@ -24,9 +24,11 @@ import { axiosPost, createFormData } from '../../utils/Apis/axios';
|
|
|
24
24
|
import { ToastBottomHelper } from '../../utils/Utils';
|
|
25
25
|
import { TESTID } from '../../configs/Constants';
|
|
26
26
|
import styles from './AddSubUnitStyles';
|
|
27
|
+
import useKeyboardShow from '../../hooks/Common/useKeyboardShow';
|
|
27
28
|
|
|
28
29
|
const AddSubUnit = ({ route }) => {
|
|
29
30
|
const t = useTranslations();
|
|
31
|
+
const { dismissKeyboard } = useKeyboardShow();
|
|
30
32
|
const { navigate, goBack } = useNavigation();
|
|
31
33
|
const { unit, addType, isAddUnit, location = '' } = route?.params;
|
|
32
34
|
const [roomName, setRoomName] = useState('');
|
|
@@ -126,8 +128,9 @@ const AddSubUnit = ({ route }) => {
|
|
|
126
128
|
]);
|
|
127
129
|
|
|
128
130
|
const onChoosePhoto = useCallback(() => {
|
|
131
|
+
dismissKeyboard();
|
|
129
132
|
setShowImagePicker(true);
|
|
130
|
-
}, []);
|
|
133
|
+
}, [dismissKeyboard]);
|
|
131
134
|
|
|
132
135
|
useEffect(() => {
|
|
133
136
|
if (imageUrl) {
|
|
@@ -122,12 +122,12 @@ const UnitDetail = ({ route }) => {
|
|
|
122
122
|
if (success) {
|
|
123
123
|
setListAutomate([
|
|
124
124
|
{
|
|
125
|
-
text: t(AUTOMATE_TYPE.
|
|
125
|
+
text: t(AUTOMATE_TYPE.ONE_TAP),
|
|
126
126
|
data: data.filter((item) => item.type === AUTOMATE_TYPE.ONE_TAP),
|
|
127
127
|
type: AUTOMATE_TYPE.ONE_TAP,
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
|
-
text: t(AUTOMATE_TYPE.
|
|
130
|
+
text: t(AUTOMATE_TYPE.SCENARIO),
|
|
131
131
|
data: data.filter((item) => item.type !== AUTOMATE_TYPE.ONE_TAP),
|
|
132
132
|
type: AUTOMATE_TYPE.AUTOMATION,
|
|
133
133
|
},
|
|
@@ -14,9 +14,10 @@ import Routes from '../../utils/Route';
|
|
|
14
14
|
import { useNavigation } from '@react-navigation/native';
|
|
15
15
|
import { axiosDelete, axiosGet } from '../../utils/Apis/axios';
|
|
16
16
|
import { SmartAccountItem } from './SmartAccountItem';
|
|
17
|
-
import { usePopover } from '../../hooks/Common';
|
|
18
|
-
import { MenuActionMore } from '../../commons';
|
|
17
|
+
import { usePopover, useBoolean } from '../../hooks/Common';
|
|
18
|
+
import { MenuActionMore, AlertAction } from '../../commons';
|
|
19
19
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
20
|
+
import { useStateAlertRemove } from '../Unit/hook/useStateAlertRemove';
|
|
20
21
|
|
|
21
22
|
const ListSmartAccount = ({ route }) => {
|
|
22
23
|
const { unitId } = route?.params || {};
|
|
@@ -36,6 +37,9 @@ const ListSmartAccount = ({ route }) => {
|
|
|
36
37
|
|
|
37
38
|
const { childRef, showingPopover, showPopoverWithRef, hidePopover } =
|
|
38
39
|
usePopover();
|
|
40
|
+
const [lockShowing, acquireLockShowing, releaseLockShowing] = useBoolean();
|
|
41
|
+
const { stateAlertRemove, onShowRemoveAlert, hideAlertAction } =
|
|
42
|
+
useStateAlertRemove();
|
|
39
43
|
|
|
40
44
|
useEffect(() => {
|
|
41
45
|
getAllSmartAccounts();
|
|
@@ -50,29 +54,31 @@ const ListSmartAccount = ({ route }) => {
|
|
|
50
54
|
[showPopoverWithRef]
|
|
51
55
|
);
|
|
52
56
|
|
|
53
|
-
const deleteSmartAccount = useCallback(async () => {
|
|
54
|
-
if (!smartAccountRef?.current) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const id = smartAccountRef.current.id;
|
|
58
|
-
const { success } = await axiosDelete(
|
|
59
|
-
API.SMART_ACCOUNT.REMOVE_SMART_ACCOUNT(id)
|
|
60
|
-
);
|
|
61
|
-
success && getAllSmartAccounts();
|
|
62
|
-
}, [getAllSmartAccounts]);
|
|
63
|
-
|
|
64
57
|
const onItemClick = useCallback(
|
|
65
58
|
(item) => {
|
|
66
59
|
if (!smartAccountRef?.current) {
|
|
67
60
|
return;
|
|
68
61
|
}
|
|
69
62
|
if (item.action === 'remove') {
|
|
70
|
-
|
|
63
|
+
acquireLockShowing();
|
|
64
|
+
onShowRemoveAlert(smartAccountRef.current.brand)();
|
|
71
65
|
}
|
|
72
66
|
},
|
|
73
|
-
[
|
|
67
|
+
[acquireLockShowing, onShowRemoveAlert]
|
|
74
68
|
);
|
|
75
69
|
|
|
70
|
+
const deleteSmartAccount = useCallback(async () => {
|
|
71
|
+
hideAlertAction();
|
|
72
|
+
if (!smartAccountRef?.current) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const id = smartAccountRef.current.id;
|
|
76
|
+
const { success } = await axiosDelete(
|
|
77
|
+
API.SMART_ACCOUNT.REMOVE_SMART_ACCOUNT(id)
|
|
78
|
+
);
|
|
79
|
+
success && getAllSmartAccounts();
|
|
80
|
+
}, [getAllSmartAccounts, hideAlertAction]);
|
|
81
|
+
|
|
76
82
|
const listMenuItem = useMemo(() => {
|
|
77
83
|
return [{ action: 'remove', text: t('remove_account') }];
|
|
78
84
|
}, [t]);
|
|
@@ -88,6 +94,7 @@ const ListSmartAccount = ({ route }) => {
|
|
|
88
94
|
},
|
|
89
95
|
[navigate, unitId]
|
|
90
96
|
);
|
|
97
|
+
|
|
91
98
|
return (
|
|
92
99
|
<View style={styles.wrap}>
|
|
93
100
|
<WrapHeaderScrollable
|
|
@@ -109,12 +116,26 @@ const ListSmartAccount = ({ route }) => {
|
|
|
109
116
|
);
|
|
110
117
|
})}
|
|
111
118
|
</View>
|
|
119
|
+
<AlertAction
|
|
120
|
+
visible={stateAlertRemove.visible && !lockShowing}
|
|
121
|
+
hideModal={hideAlertAction}
|
|
122
|
+
title={stateAlertRemove.title}
|
|
123
|
+
message={stateAlertRemove.message}
|
|
124
|
+
leftButtonTitle={stateAlertRemove.leftButton}
|
|
125
|
+
leftButtonClick={deleteSmartAccount}
|
|
126
|
+
rightButtonTitle={stateAlertRemove.rightButton}
|
|
127
|
+
rightButtonClick={hideAlertAction}
|
|
128
|
+
boxLeftButtonStyle={styles.boxButtonNotSelect}
|
|
129
|
+
boxRightButtonStyle={styles.boxButtonSelect}
|
|
130
|
+
rightButtonStyle={styles.textRightButton}
|
|
131
|
+
/>
|
|
112
132
|
<MenuActionMore
|
|
113
133
|
isVisible={showingPopover}
|
|
114
134
|
hideMore={hidePopover}
|
|
115
135
|
listMenuItem={listMenuItem}
|
|
116
136
|
childRef={childRef}
|
|
117
137
|
onItemClick={onItemClick}
|
|
138
|
+
hideComplete={releaseLockShowing}
|
|
118
139
|
/>
|
|
119
140
|
</WrapHeaderScrollable>
|
|
120
141
|
</View>
|
|
@@ -42,4 +42,24 @@ export default StyleSheet.create({
|
|
|
42
42
|
borderColor: Colors.ShadownTransparent,
|
|
43
43
|
borderTopWidth: 1,
|
|
44
44
|
},
|
|
45
|
+
boxButtonNotSelect: {
|
|
46
|
+
marginVertical: 16,
|
|
47
|
+
height: 45,
|
|
48
|
+
borderRadius: 30,
|
|
49
|
+
borderColor: Colors.Primary,
|
|
50
|
+
borderWidth: 1,
|
|
51
|
+
paddingVertical: 0,
|
|
52
|
+
},
|
|
53
|
+
boxButtonSelect: {
|
|
54
|
+
marginVertical: 16,
|
|
55
|
+
height: 45,
|
|
56
|
+
borderRadius: 30,
|
|
57
|
+
borderColor: Colors.Primary,
|
|
58
|
+
backgroundColor: Colors.Primary,
|
|
59
|
+
borderWidth: 1,
|
|
60
|
+
paddingVertical: 0,
|
|
61
|
+
},
|
|
62
|
+
textRightButton: {
|
|
63
|
+
color: Colors.White,
|
|
64
|
+
},
|
|
45
65
|
});
|
|
@@ -5,10 +5,10 @@ export const useStateAlertRemove = () => {
|
|
|
5
5
|
const t = useTranslations();
|
|
6
6
|
const [stateAlertRemove, setStateAlertRemove] = useState({
|
|
7
7
|
visible: false,
|
|
8
|
-
title: t('
|
|
8
|
+
title: t('remove_account'),
|
|
9
9
|
message: '',
|
|
10
|
-
leftButton: t('
|
|
11
|
-
rightButton: t('
|
|
10
|
+
leftButton: t('yes_remove'),
|
|
11
|
+
rightButton: t('cancel'),
|
|
12
12
|
});
|
|
13
13
|
const hideAlertAction = useCallback(() => {
|
|
14
14
|
setStateAlertRemove((state) => ({
|
|
@@ -22,9 +22,7 @@ export const useStateAlertRemove = () => {
|
|
|
22
22
|
setStateAlertRemove((state) => ({
|
|
23
23
|
...state,
|
|
24
24
|
visible: true,
|
|
25
|
-
message:
|
|
26
|
-
text: brand === 'google_home' ? 'Điện Quang' : brand,
|
|
27
|
-
})}`,
|
|
25
|
+
message: t('this_action_will_remove_all_related_devices'),
|
|
28
26
|
}));
|
|
29
27
|
},
|
|
30
28
|
[t]
|
|
@@ -798,7 +798,7 @@
|
|
|
798
798
|
"smart": "Smart",
|
|
799
799
|
"smart_account": "Smart account",
|
|
800
800
|
"delete_smart_account": "Delete smart account",
|
|
801
|
-
"
|
|
801
|
+
"this_action_will_remove_all_related_devices": "This action will remove all related devices and it’s setting forever. Do you wish to continue?",
|
|
802
802
|
"multi_unit": "Multi Units",
|
|
803
803
|
"edit_device": "Edit Device",
|
|
804
804
|
"device_name": "Device name",
|
|
@@ -888,5 +888,6 @@
|
|
|
888
888
|
"location_permission_required_wifi_title": "Location permission is required for WiFi connections",
|
|
889
889
|
"location_permission_required_wifi_message": "This app needs location permission as this is required to scan for wifi networks.",
|
|
890
890
|
"deny": "DENY",
|
|
891
|
-
"allow": "ALLOW"
|
|
891
|
+
"allow": "ALLOW",
|
|
892
|
+
"yes_remove": "Yes,remove"
|
|
892
893
|
}
|
|
@@ -799,7 +799,7 @@
|
|
|
799
799
|
"smart": "Thông minh",
|
|
800
800
|
"smart_account": "Tài khoản thông minh",
|
|
801
801
|
"delete_smart_account": "Xóa tài khoản thông minh",
|
|
802
|
-
"
|
|
802
|
+
"this_action_will_remove_all_related_devices": "Hành động này sẽ xóa tất cả các thiết bị liên quan và nó sẽ cài đặt vĩnh viễn. Bạn có muốn tiếp tục không?",
|
|
803
803
|
"edit_device": "Chỉnh sửa thiết bị",
|
|
804
804
|
"device_name": "Tên thiết bị",
|
|
805
805
|
"rename_successfully": "Đổi tên thành công!",
|
|
@@ -890,5 +890,6 @@
|
|
|
890
890
|
"location_permission_required_wifi_title": "Quyền vị trí là bắt buộc đối với kết nối WiFi",
|
|
891
891
|
"location_permission_required_wifi_message": "Ứng dụng này cần có quyền định vị vì ứng dụng này được yêu cầu để quét các mạng Wi-Fi.",
|
|
892
892
|
"deny": "Từ chối",
|
|
893
|
-
"allow": "Cho phép"
|
|
893
|
+
"allow": "Cho phép",
|
|
894
|
+
"yes_remove": "Có,loại bỏ"
|
|
894
895
|
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import renderer, { act } from 'react-test-renderer';
|
|
3
|
-
|
|
4
|
-
import MenuActionAddSchedule from '../hooks/MenuActionAddSchedule';
|
|
5
|
-
import { SCProvider } from '../../../context';
|
|
6
|
-
import { mockSCStore } from '../../../context/mockStore';
|
|
7
|
-
import RecurringDetail from '../hooks/RecurringDetail';
|
|
8
|
-
import WheelDateTimePicker from '../../WheelDateTimePicker';
|
|
9
|
-
|
|
10
|
-
const mockSetState = jest.fn();
|
|
11
|
-
jest.mock('react', () => {
|
|
12
|
-
return {
|
|
13
|
-
...jest.requireActual('react'),
|
|
14
|
-
memo: (x) => x,
|
|
15
|
-
useState: jest.fn((init) => [init, mockSetState]),
|
|
16
|
-
};
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const wrapComponent = (actionGroup) => (
|
|
20
|
-
<SCProvider initState={mockSCStore({})}>
|
|
21
|
-
<MenuActionAddSchedule />
|
|
22
|
-
</SCProvider>
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
const mockedNavigate = jest.fn();
|
|
26
|
-
|
|
27
|
-
jest.mock('@react-navigation/native', () => {
|
|
28
|
-
return {
|
|
29
|
-
...jest.requireActual('@react-navigation/native'),
|
|
30
|
-
useNavigation: () => ({
|
|
31
|
-
goBack: mockedNavigate,
|
|
32
|
-
}),
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
jest.mock('react', () => {
|
|
37
|
-
return {
|
|
38
|
-
...jest.requireActual('react'),
|
|
39
|
-
memo: (x) => x,
|
|
40
|
-
};
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
jest.mock('axios');
|
|
44
|
-
|
|
45
|
-
describe('Test MenuActionAddSchedule', () => {
|
|
46
|
-
let tree;
|
|
47
|
-
|
|
48
|
-
test('test render MenuActionAddSchedule', async () => {
|
|
49
|
-
await act(async () => {
|
|
50
|
-
tree = await renderer.create(wrapComponent());
|
|
51
|
-
});
|
|
52
|
-
const instance = tree.root;
|
|
53
|
-
|
|
54
|
-
const recurringDetail = instance.findByType(RecurringDetail);
|
|
55
|
-
await act(async () => {
|
|
56
|
-
recurringDetail.props.onShowSetDateTime();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const wheelDateTimePicker = instance.findByType(WheelDateTimePicker);
|
|
60
|
-
|
|
61
|
-
await act(async () => {
|
|
62
|
-
wheelDateTimePicker.props.onPicked();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
await act(async () => {
|
|
66
|
-
wheelDateTimePicker.props.onCancel();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
expect(wheelDateTimePicker.props.isVisible).toEqual(false);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { StyleSheet } from 'react-native';
|
|
2
|
-
import { Colors } from '../../../configs';
|
|
3
|
-
|
|
4
|
-
export default StyleSheet.create({
|
|
5
|
-
title: {
|
|
6
|
-
marginTop: 16,
|
|
7
|
-
},
|
|
8
|
-
value: {
|
|
9
|
-
marginTop: 8,
|
|
10
|
-
marginBottom: 12,
|
|
11
|
-
},
|
|
12
|
-
repeatWrap: {
|
|
13
|
-
flexDirection: 'row',
|
|
14
|
-
justifyContent: 'space-around',
|
|
15
|
-
marginTop: 16,
|
|
16
|
-
},
|
|
17
|
-
repeatItem: {
|
|
18
|
-
width: 24,
|
|
19
|
-
height: 24,
|
|
20
|
-
justifyContent: 'center',
|
|
21
|
-
alignItems: 'center',
|
|
22
|
-
borderRadius: 12,
|
|
23
|
-
borderWidth: 0,
|
|
24
|
-
borderColor: Colors.Orange,
|
|
25
|
-
},
|
|
26
|
-
repeatItemSelected: {
|
|
27
|
-
borderWidth: 1,
|
|
28
|
-
},
|
|
29
|
-
repeatText: {
|
|
30
|
-
lineHeight: 16,
|
|
31
|
-
},
|
|
32
|
-
curtainText: {
|
|
33
|
-
fontSize: 17,
|
|
34
|
-
marginTop: 12,
|
|
35
|
-
color: Colors.Gray9,
|
|
36
|
-
},
|
|
37
|
-
boder: {
|
|
38
|
-
borderBottomWidth: 1,
|
|
39
|
-
borderColor: Colors.Gray4,
|
|
40
|
-
},
|
|
41
|
-
});
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import React, { memo, useCallback, useState } from 'react';
|
|
2
|
-
import { View } from 'react-native';
|
|
3
|
-
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
4
|
-
|
|
5
|
-
import Text from '../../Text';
|
|
6
|
-
import { ModalCustom } from '../../Modal';
|
|
7
|
-
import ViewButtonBottom from '../../ViewButtonBottom';
|
|
8
|
-
import WheelDateTimePicker from '../../WheelDateTimePicker';
|
|
9
|
-
import moment from 'moment';
|
|
10
|
-
import { useBoolean } from '../../../hooks/Common';
|
|
11
|
-
import RecurringDetail from './RecurringDetail';
|
|
12
|
-
import _TextInput from '../../../commons/Form/TextInput';
|
|
13
|
-
import styles from './MenuActionAddScheduleStyle';
|
|
14
|
-
|
|
15
|
-
const MenuActionAddSchedule = memo(({ visible, hideModal, onItemClick }) => {
|
|
16
|
-
const t = useTranslations();
|
|
17
|
-
|
|
18
|
-
const [stateDateTimePicker, setStateDateTimePicker] = useState({
|
|
19
|
-
isVisible: false,
|
|
20
|
-
mode: 'time',
|
|
21
|
-
defaultValue: moment().valueOf(),
|
|
22
|
-
setter: null,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const [lockShowing, releaseLockShowing, acquireLockShowing] =
|
|
26
|
-
useBoolean(true);
|
|
27
|
-
const [recurringTimeStart, setRecurringTimeStart] = useState('');
|
|
28
|
-
const [recurringTimeEnd, setRecurringTimeEnd] = useState('');
|
|
29
|
-
const [recurringTimeRepeat, setRecurringTimeRepeat] = useState('');
|
|
30
|
-
|
|
31
|
-
const onShowSetDateTime = useCallback(
|
|
32
|
-
(currentValue, setter, mode) => {
|
|
33
|
-
acquireLockShowing();
|
|
34
|
-
setStateDateTimePicker((state) => ({
|
|
35
|
-
...state,
|
|
36
|
-
isVisible: true,
|
|
37
|
-
mode: mode,
|
|
38
|
-
defaultValue: currentValue,
|
|
39
|
-
setter: setter,
|
|
40
|
-
}));
|
|
41
|
-
},
|
|
42
|
-
[setStateDateTimePicker, acquireLockShowing]
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
const onDateTimePicked = useCallback(
|
|
46
|
-
(timeData) => {
|
|
47
|
-
const setter = stateDateTimePicker.setter;
|
|
48
|
-
setter && setter(moment(timeData));
|
|
49
|
-
},
|
|
50
|
-
[stateDateTimePicker]
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
const onHideSetDateTime = useCallback(() => {
|
|
54
|
-
acquireLockShowing();
|
|
55
|
-
setStateDateTimePicker((state) => ({
|
|
56
|
-
...state,
|
|
57
|
-
isVisible: false,
|
|
58
|
-
}));
|
|
59
|
-
}, [setStateDateTimePicker, acquireLockShowing]);
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<ModalCustom
|
|
63
|
-
isVisible={visible}
|
|
64
|
-
onBackButtonPress={hideModal}
|
|
65
|
-
onBackdropPress={hideModal}
|
|
66
|
-
style={styles.container}
|
|
67
|
-
>
|
|
68
|
-
<View style={styles.popoverStyle}>
|
|
69
|
-
<View style={styles.modalWrapper}>
|
|
70
|
-
<View style={styles.modalHeader}>
|
|
71
|
-
<Text style={styles.modalHeaderText}>{t('add_schedule')}</Text>
|
|
72
|
-
<View style={styles.boder} />
|
|
73
|
-
<RecurringDetail
|
|
74
|
-
onShowSetDateTime={onShowSetDateTime}
|
|
75
|
-
recurringTimeStart={recurringTimeStart}
|
|
76
|
-
recurringTimeEnd={recurringTimeEnd}
|
|
77
|
-
recurringTimeRepeat={recurringTimeRepeat}
|
|
78
|
-
setRecurringTimeStart={setRecurringTimeStart}
|
|
79
|
-
setRecurringTimeEnd={setRecurringTimeEnd}
|
|
80
|
-
setRecurringTimeRepeat={setRecurringTimeRepeat}
|
|
81
|
-
/>
|
|
82
|
-
|
|
83
|
-
<Text style={styles.subName}>{t('schedule_name')}</Text>
|
|
84
|
-
<_TextInput
|
|
85
|
-
placeholder={t('enter_name')}
|
|
86
|
-
wrapStyle={styles.noMarginTop}
|
|
87
|
-
textInputStyle={styles.textInput}
|
|
88
|
-
/>
|
|
89
|
-
<View style={styles.ViewButtonBottom}>
|
|
90
|
-
<ViewButtonBottom
|
|
91
|
-
leftTitle={t('cancel')}
|
|
92
|
-
onLeftClick={hideModal}
|
|
93
|
-
rightTitle={t('done')}
|
|
94
|
-
/>
|
|
95
|
-
</View>
|
|
96
|
-
<WheelDateTimePicker
|
|
97
|
-
mode={stateDateTimePicker.mode}
|
|
98
|
-
isVisible={stateDateTimePicker.isVisible && lockShowing}
|
|
99
|
-
defaultValue={stateDateTimePicker.defaultValue}
|
|
100
|
-
onPicked={onDateTimePicked}
|
|
101
|
-
onCancel={onHideSetDateTime}
|
|
102
|
-
onHide={releaseLockShowing}
|
|
103
|
-
/>
|
|
104
|
-
</View>
|
|
105
|
-
</View>
|
|
106
|
-
</View>
|
|
107
|
-
</ModalCustom>
|
|
108
|
-
);
|
|
109
|
-
});
|
|
110
|
-
export default MenuActionAddSchedule;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { Colors } from '../../../configs';
|
|
2
|
-
import { StyleSheet } from 'react-native';
|
|
3
|
-
|
|
4
|
-
export default StyleSheet.create({
|
|
5
|
-
container: {
|
|
6
|
-
flex: 1,
|
|
7
|
-
margin: 0,
|
|
8
|
-
},
|
|
9
|
-
popoverStyle: {
|
|
10
|
-
width: '100%',
|
|
11
|
-
backgroundColor: Colors.White,
|
|
12
|
-
bottom: 0,
|
|
13
|
-
left: 0,
|
|
14
|
-
position: 'absolute',
|
|
15
|
-
borderRadius: 10,
|
|
16
|
-
},
|
|
17
|
-
modalWrapper: {
|
|
18
|
-
flex: 1,
|
|
19
|
-
flexDirection: 'column',
|
|
20
|
-
backgroundColor: Colors.White,
|
|
21
|
-
borderRadius: 10,
|
|
22
|
-
},
|
|
23
|
-
modalHeader: {
|
|
24
|
-
padding: 16,
|
|
25
|
-
backgroundColor: Colors.White,
|
|
26
|
-
justifyContent: 'flex-start',
|
|
27
|
-
},
|
|
28
|
-
modalHeaderText: {
|
|
29
|
-
fontSize: 17,
|
|
30
|
-
lineHeight: 24,
|
|
31
|
-
color: Colors.Gray9,
|
|
32
|
-
fontWeight: 'bold',
|
|
33
|
-
borderTopLeftRadius: 20,
|
|
34
|
-
borderTopRightRadius: 20,
|
|
35
|
-
marginVertical: 20,
|
|
36
|
-
},
|
|
37
|
-
boder: {
|
|
38
|
-
borderBottomWidth: 1,
|
|
39
|
-
borderColor: Colors.Gray4,
|
|
40
|
-
},
|
|
41
|
-
subName: {
|
|
42
|
-
color: Colors.Gray8,
|
|
43
|
-
marginTop: 12,
|
|
44
|
-
},
|
|
45
|
-
ViewButtonBottom: {
|
|
46
|
-
borderTopWidth: 1,
|
|
47
|
-
borderTopColor: Colors.Gray4,
|
|
48
|
-
},
|
|
49
|
-
scheduleName: {
|
|
50
|
-
fontSize: 17,
|
|
51
|
-
marginTop: 12,
|
|
52
|
-
color: Colors.Gray9,
|
|
53
|
-
marginBottom: 12,
|
|
54
|
-
},
|
|
55
|
-
noMarginTop: {
|
|
56
|
-
marginTop: 0,
|
|
57
|
-
backgroundColor: Colors.White,
|
|
58
|
-
},
|
|
59
|
-
textInput: {
|
|
60
|
-
marginTop: 0,
|
|
61
|
-
borderWidth: 0,
|
|
62
|
-
borderBottomWidth: 1,
|
|
63
|
-
borderBottomColor: Colors.Gray4,
|
|
64
|
-
paddingLeft: 0,
|
|
65
|
-
backgroundColor: Colors.White,
|
|
66
|
-
fontSize: 17,
|
|
67
|
-
color: Colors.Gray9,
|
|
68
|
-
},
|
|
69
|
-
});
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import React, { useCallback } from 'react';
|
|
2
|
-
import { View, TouchableOpacity } from 'react-native';
|
|
3
|
-
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
4
|
-
|
|
5
|
-
import Text from '../../../commons/Text';
|
|
6
|
-
import { Colors } from '../../../configs';
|
|
7
|
-
import { REPEAT_ITEMS } from '../../../screens/GuestInfo/constant';
|
|
8
|
-
import styles from './AccessScheduleDetailStyles';
|
|
9
|
-
|
|
10
|
-
const RecurringDetail = ({
|
|
11
|
-
onShowSetDateTime,
|
|
12
|
-
recurringTimeStart,
|
|
13
|
-
recurringTimeEnd,
|
|
14
|
-
recurringTimeRepeat,
|
|
15
|
-
setRecurringTimeStart,
|
|
16
|
-
setRecurringTimeEnd,
|
|
17
|
-
setRecurringTimeRepeat,
|
|
18
|
-
}) => {
|
|
19
|
-
const t = useTranslations();
|
|
20
|
-
const onSetTimeStart = useCallback(() => {
|
|
21
|
-
onShowSetDateTime();
|
|
22
|
-
}, [onShowSetDateTime]);
|
|
23
|
-
|
|
24
|
-
const onSetTimeEnd = useCallback(() => {
|
|
25
|
-
onShowSetDateTime();
|
|
26
|
-
}, [onShowSetDateTime]);
|
|
27
|
-
|
|
28
|
-
const onSetRepeat = useCallback(
|
|
29
|
-
(item) => {
|
|
30
|
-
const index = recurringTimeRepeat.indexOf(item.value);
|
|
31
|
-
if (index !== -1) {
|
|
32
|
-
setRecurringTimeRepeat([
|
|
33
|
-
...recurringTimeRepeat.slice(0, index),
|
|
34
|
-
...recurringTimeRepeat.slice(index + 1),
|
|
35
|
-
]);
|
|
36
|
-
} else {
|
|
37
|
-
setRecurringTimeRepeat([...recurringTimeRepeat, item.value]);
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
[recurringTimeRepeat, setRecurringTimeRepeat]
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const renderRepeatItem = useCallback(
|
|
44
|
-
(item, index, isSelected) => {
|
|
45
|
-
return (
|
|
46
|
-
<TouchableOpacity
|
|
47
|
-
key={index}
|
|
48
|
-
style={[styles.repeatItem, isSelected && styles.repeatItemSelected]}
|
|
49
|
-
onPress={() => onSetRepeat(item)}
|
|
50
|
-
>
|
|
51
|
-
<Text
|
|
52
|
-
type="Body"
|
|
53
|
-
lineHeight={16}
|
|
54
|
-
color={isSelected ? Colors.Orange : item.color}
|
|
55
|
-
style={styles.repeatText}
|
|
56
|
-
>
|
|
57
|
-
{item.text}
|
|
58
|
-
</Text>
|
|
59
|
-
</TouchableOpacity>
|
|
60
|
-
);
|
|
61
|
-
},
|
|
62
|
-
[onSetRepeat]
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<View>
|
|
67
|
-
<Text style={styles.curtainText}>{t('curtain_opens')}</Text>
|
|
68
|
-
<TouchableOpacity onPress={onSetTimeStart}>
|
|
69
|
-
<Text type="Body" color={Colors.Orange} style={styles.value}>
|
|
70
|
-
07:00 AM
|
|
71
|
-
</Text>
|
|
72
|
-
</TouchableOpacity>
|
|
73
|
-
<View style={styles.boder} />
|
|
74
|
-
<Text style={styles.curtainText}>{t('curtain_closes')}</Text>
|
|
75
|
-
<TouchableOpacity onPress={onSetTimeEnd}>
|
|
76
|
-
<Text type="Body" color={Colors.Orange} style={styles.value}>
|
|
77
|
-
12:00 AM
|
|
78
|
-
</Text>
|
|
79
|
-
</TouchableOpacity>
|
|
80
|
-
<View style={styles.boder} />
|
|
81
|
-
<Text type="Body" color={Colors.Gray8} style={styles.title}>
|
|
82
|
-
{t('repeat')}
|
|
83
|
-
</Text>
|
|
84
|
-
<View style={styles.repeatWrap}>
|
|
85
|
-
{REPEAT_ITEMS.map((item, index) =>
|
|
86
|
-
renderRepeatItem(
|
|
87
|
-
item,
|
|
88
|
-
index,
|
|
89
|
-
recurringTimeRepeat.includes(item.value)
|
|
90
|
-
)
|
|
91
|
-
)}
|
|
92
|
-
</View>
|
|
93
|
-
</View>
|
|
94
|
-
);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export default RecurringDetail;
|