@eohjsc/react-native-smart-city 0.2.96 → 0.2.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/README.md +35 -14
- package/package.json +16 -4
- package/src/commons/Action/ItemQuickAction.js +5 -2
- package/src/commons/ActionGroup/ColorPickerTemplate.js +1 -1
- package/src/commons/ActionGroup/NumberUpDownActionTemplate.js +12 -4
- package/src/commons/ActionGroup/OnOffSmartLock/OnOffSmartLock.js +7 -4
- package/src/commons/ActionGroup/OnOffSmartLock/SetupGeneratePasscode/index.js +1 -0
- package/src/commons/ActionGroup/OnOffTemplate/OnOffSimpleTemplate.js +10 -10
- package/src/commons/ActionGroup/OnOffTemplate/index.js +18 -15
- package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +8 -2
- package/src/commons/ActionGroup/SliderRangeTemplate.js +1 -1
- package/src/commons/ActionGroup/SmartTiviActionTemplate/SmartTiviActionTemplate.js +4 -1
- package/src/commons/ActionGroup/StatesGridActionTemplate.js +14 -4
- package/src/commons/ActionGroup/TimerActionTemplate.js +9 -1
- package/src/commons/ActionGroup/TwoButtonTemplate/index.js +13 -9
- package/src/commons/ActionGroup/__test__/OnOffButtonTemplate.test.js +14 -14
- package/src/commons/ActionGroup/__test__/OnOffTemplate.test.js +53 -78
- package/src/commons/ActionGroup/__test__/OneBigButtonTemplate.test.js +36 -20
- package/src/commons/Auth/AccountList.js +1 -1
- package/src/commons/{Connecting → Connecting}/__test__/Connecting.test.js +0 -0
- package/src/commons/{Connecting → Connecting}/index.js +0 -0
- package/src/commons/{Connecting → Connecting}/styles.js +0 -0
- package/src/commons/ConnectingProcess/index.js +1 -1
- package/src/commons/Device/HistoryChart.js +6 -2
- package/src/commons/Device/PMSensor/PMSensorIndicatior.js +16 -12
- package/src/commons/Device/PMSensor/PMSensorIndicatorStyles.js +3 -0
- package/src/commons/Device/WaterQualitySensor/ListQualityIndicator.js +1 -0
- package/src/commons/FieldTemplate/ChooseUserField/ChooseFieldStyles.js +25 -0
- package/src/commons/FieldTemplate/ChooseUserField/ChoosePopup.js +96 -0
- package/src/commons/FieldTemplate/ChooseUserField/ChoosePopupStyles.js +39 -0
- package/src/commons/FieldTemplate/ChooseUserField/__test__/index.test.js +113 -0
- package/src/commons/FieldTemplate/ChooseUserField/index.js +62 -0
- package/src/commons/FieldTemplate/PasscodeField/PasscodeFieldStyles.js +30 -0
- package/src/commons/FieldTemplate/PasscodeField/__test__/index.test.js +93 -0
- package/src/commons/FieldTemplate/PasscodeField/index.js +43 -0
- package/src/commons/FieldTemplate/ScheduleField/ScheduleFieldStyles.js +13 -0
- package/src/commons/FieldTemplate/ScheduleField/__test__/index.test.js +182 -0
- package/src/commons/FieldTemplate/ScheduleField/index.js +176 -0
- package/src/commons/FullLoading/index.js +2 -1
- package/src/commons/MenuActionAddnew/index.js +1 -0
- package/src/commons/MenuActionList/index.js +1 -0
- package/src/commons/MenuActionMore/index.js +1 -1
- package/src/commons/PreventAccess/__test__/PreventAccess.test.js +62 -0
- package/src/commons/PreventAccess/index.js +67 -0
- package/src/commons/PreventAccess/styles.js +33 -0
- package/src/commons/WheelDateTimePicker/index.js +2 -1
- package/src/configs/API.js +3 -0
- package/src/configs/Constants.js +16 -1
- package/src/iot/RemoteControl/GoogleHome.js +24 -11
- package/src/iot/RemoteControl/__test__/GoogleHome.test.js +32 -0
- package/src/navigations/UnitStack.js +8 -0
- package/src/screens/AQIGuide/index.js +1 -1
- package/src/screens/ActivityLog/FilterPopup.js +2 -0
- package/src/screens/AddCommon/SelectSubUnit.js +1 -0
- package/src/screens/AddCommon/SelectUnit.js +1 -0
- package/src/screens/AddLocationMaps/index.js +4 -1
- package/src/screens/AddNewAction/SelectSensorDevices.js +14 -3
- package/src/screens/AddNewAction/__test__/SelectSensorDevices.test.js +34 -92
- package/src/screens/AddNewAutoSmart/__test__/AddNewAutoSmart.test.js +3 -1
- package/src/screens/AddNewAutoSmart/index.js +5 -2
- package/src/screens/AddNewDevice/index.js +1 -0
- package/src/screens/AddNewGateway/PlugAndPlay/ConnectWifiWarning.js +1 -1
- package/src/screens/AddNewGateway/PlugAndPlay/GatewayWifiList.js +4 -1
- package/src/screens/AddNewGateway/SelectGateway.js +1 -0
- package/src/screens/AddNewGateway/SetupGatewayWifi.js +1 -0
- package/src/screens/AddNewGateway/index.js +1 -0
- package/src/screens/AddNewOneTap/__test__/AddNewOneTap.test.js +1 -1
- package/src/screens/AddNewOneTap/index.js +3 -2
- package/src/screens/Automate/index.js +2 -0
- package/src/screens/Device/__test__/detail.test.js +4 -4
- package/src/screens/Device/detail.js +44 -6
- package/src/screens/EmergencyContacts/EmergencyContactsSelectContacts.js +5 -2
- package/src/screens/EmergencyContacts/__test__/EmergencyContactList.test.js +14 -0
- package/src/screens/EmergencyContacts/__test__/EmergencyContactsSelectContacts.test.js +19 -1
- package/src/screens/EmergencySetting/index.js +4 -1
- package/src/screens/Explore/index.js +2 -0
- package/src/screens/GuestInfo/__test__/index.test.js +1 -1
- package/src/screens/GuestInfo/components/RecurringDetail.js +1 -0
- package/src/screens/GuestInfo/components/TemporaryDetail.js +2 -2
- package/src/screens/ManageAccess/index.js +1 -0
- package/src/screens/MoveToAnotherSubUnit/index.js +1 -1
- package/src/screens/ScanChipQR/components/QRScan/index.js +1 -0
- package/src/screens/ScriptDetail/index.js +3 -3
- package/src/screens/SelectUnit/__test__/index.test.js +1 -1
- package/src/screens/SelectUnit/index.js +5 -2
- package/src/screens/SetSchedule/index.js +6 -2
- package/src/screens/SharedUnit/index.js +2 -0
- package/src/screens/Sharing/MemberList.js +12 -11
- package/src/screens/Sharing/SelectPermission.js +1 -1
- package/src/screens/Sharing/hooks/index.js +3 -0
- package/src/screens/SideMenuDetail/SideMenuDetailStyles.js +28 -0
- package/src/screens/SideMenuDetail/__test__/index.test.js +165 -0
- package/src/screens/SideMenuDetail/index.js +149 -0
- package/src/screens/SmartIr/components/SelectBrand.js +1 -1
- package/src/screens/SubUnit/ManageSubUnit.js +1 -0
- package/src/screens/SyncLGDevice/AddLGDevice.js +1 -0
- package/src/screens/TDSGuide/index.js +4 -1
- package/src/screens/UVIndexGuide/index.js +1 -1
- package/src/screens/Unit/Detail.js +18 -5
- package/src/screens/Unit/SelectAddress.js +4 -1
- package/src/screens/Unit/Station/index.js +1 -0
- package/src/screens/Unit/Summaries.js +1 -1
- package/src/screens/Unit/__test__/Detail.test.js +25 -5
- package/src/screens/UnitSummary/__test__/index.test.js +32 -0
- package/src/screens/UnitSummary/components/3PPowerConsumption/index.js +1 -1
- package/src/screens/WaterQualityGuide/index.js +1 -1
- package/src/utils/I18n/translations/en.json +5 -1
- package/src/utils/I18n/translations/vi.json +5 -1
- package/src/utils/Route/index.js +1 -0
|
@@ -75,7 +75,25 @@ describe('test EmergencyContactsSelectContacts', () => {
|
|
|
75
75
|
test('test onSave emergencyContactsSelectContacts', async () => {
|
|
76
76
|
const response = {
|
|
77
77
|
status: 200,
|
|
78
|
-
|
|
78
|
+
};
|
|
79
|
+
axios.post.mockImplementation(async () => {
|
|
80
|
+
return response;
|
|
81
|
+
});
|
|
82
|
+
act(() => {
|
|
83
|
+
tree = create(wrapComponent(route));
|
|
84
|
+
});
|
|
85
|
+
const instance = tree.root;
|
|
86
|
+
|
|
87
|
+
const viewButtonBottom = instance.findByType(ViewButtonBottom);
|
|
88
|
+
await act(async () => {
|
|
89
|
+
viewButtonBottom.props.onRightClick();
|
|
90
|
+
});
|
|
91
|
+
expect(axios.post).toHaveBeenCalled();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('test onSave emergencyContactsSelectContacts fail', async () => {
|
|
95
|
+
const response = {
|
|
96
|
+
status: 500,
|
|
79
97
|
};
|
|
80
98
|
axios.post.mockImplementation(async () => {
|
|
81
99
|
return response;
|
|
@@ -63,7 +63,10 @@ const EmergencySetting = () => {
|
|
|
63
63
|
<View style={styles.wrap}>
|
|
64
64
|
<HeaderCustom title={t('setting')} isShowSeparator />
|
|
65
65
|
|
|
66
|
-
<ScrollView
|
|
66
|
+
<ScrollView
|
|
67
|
+
contentContainerStyle={styles.contentContainerStyle}
|
|
68
|
+
scrollIndicatorInsets={{ right: 1 }}
|
|
69
|
+
>
|
|
67
70
|
{listData.map((item, index) => (
|
|
68
71
|
<DropDownItem
|
|
69
72
|
{...item}
|
|
@@ -92,6 +92,7 @@ const Explore = ({ navigation }) => {
|
|
|
92
92
|
horizontal={true}
|
|
93
93
|
contentContainerStyle={styles.locationContent}
|
|
94
94
|
showsHorizontalScrollIndicator={false}
|
|
95
|
+
scrollIndicatorInsets={{ right: 1 }}
|
|
95
96
|
>
|
|
96
97
|
{!!unitsNearMe &&
|
|
97
98
|
unitsNearMe.map((item) => (
|
|
@@ -134,6 +135,7 @@ const Explore = ({ navigation }) => {
|
|
|
134
135
|
onEndReachedThreshold={0.01}
|
|
135
136
|
onMomentumScrollBegin={() => setOnEndReached(false)}
|
|
136
137
|
onEndReached={handleEndReachUnitPublic}
|
|
138
|
+
scrollIndicatorInsets={{ right: 1 }}
|
|
137
139
|
/>
|
|
138
140
|
</Animated.View>
|
|
139
141
|
</SafeAreaView>
|
|
@@ -152,7 +152,7 @@ describe('Test GuestInfo', () => {
|
|
|
152
152
|
);
|
|
153
153
|
const dateTimePickerDone = getButton(
|
|
154
154
|
instance,
|
|
155
|
-
`${TESTID.
|
|
155
|
+
`${TESTID.WHEEL_DATE_TIME_PICKER_BUTTON}${TESTID.VIEW_BUTTON_BOTTOM_RIGHT_BUTTON}`
|
|
156
156
|
);
|
|
157
157
|
|
|
158
158
|
// show AccessScheduleSheet
|
|
@@ -30,7 +30,7 @@ const TemporaryDetail = ({
|
|
|
30
30
|
</Text>
|
|
31
31
|
<TouchableOpacity
|
|
32
32
|
onPress={onSetTimeStart}
|
|
33
|
-
testID={TESTID.
|
|
33
|
+
testID={TESTID.TEMPORARY_START_TEXT_BUTTON}
|
|
34
34
|
>
|
|
35
35
|
<Text type="Body" color={Colors.Orange} style={styles.value}>
|
|
36
36
|
{moment(temporaryTimeStart).format('hh:mm A DD/MM/YYYY')}
|
|
@@ -41,7 +41,7 @@ const TemporaryDetail = ({
|
|
|
41
41
|
</Text>
|
|
42
42
|
<TouchableOpacity
|
|
43
43
|
onPress={onSetTimeEnd}
|
|
44
|
-
testID={TESTID.
|
|
44
|
+
testID={TESTID.TEMPORARY_END_TEXT_BUTTON}
|
|
45
45
|
>
|
|
46
46
|
<Text type="Body" color={Colors.Orange} style={styles.value}>
|
|
47
47
|
{moment(temporaryTimeEnd).format('hh:mm A DD/MM/YYYY')}
|
|
@@ -62,7 +62,7 @@ const MoveToAnotherSubUnit = memo(({ route }) => {
|
|
|
62
62
|
return (
|
|
63
63
|
<View style={styles.wrap}>
|
|
64
64
|
<HeaderCustom title={t('move_to_another_sub_unit')} isShowSeparator />
|
|
65
|
-
<ScrollView>
|
|
65
|
+
<ScrollView scrollIndicatorInsets={{ right: 1 }}>
|
|
66
66
|
<View style={styles.container}>
|
|
67
67
|
<View>
|
|
68
68
|
{listStationUnit.map((item, index) => (
|
|
@@ -369,10 +369,10 @@ const ScriptDetail = ({ route }) => {
|
|
|
369
369
|
return `${config_name} ${t(textCondition)} ${value}`;
|
|
370
370
|
} else if (type === AUTOMATE_TYPE.SCHEDULE) {
|
|
371
371
|
const time =
|
|
372
|
-
time_repeat
|
|
373
|
-
? time_repeat.substring(0, time_repeat
|
|
372
|
+
time_repeat?.length >= 8
|
|
373
|
+
? time_repeat.substring(0, time_repeat?.length - 3)
|
|
374
374
|
: time_repeat;
|
|
375
|
-
const date = date_repeat
|
|
375
|
+
const date = date_repeat?.split('-').reverse().join('/');
|
|
376
376
|
const weekday = {
|
|
377
377
|
1: t('mon'),
|
|
378
378
|
2: t('tue'),
|
|
@@ -26,9 +26,10 @@ const SelectUnit = () => {
|
|
|
26
26
|
scriptName,
|
|
27
27
|
routeName,
|
|
28
28
|
isCreateNewAction,
|
|
29
|
-
unit,
|
|
30
29
|
title,
|
|
31
30
|
automate,
|
|
31
|
+
oldType,
|
|
32
|
+
unit,
|
|
32
33
|
} = params;
|
|
33
34
|
const [data, setData] = useState([]);
|
|
34
35
|
const [selectedItem, setSelectedItem] = useState(data[0]);
|
|
@@ -49,7 +50,7 @@ const SelectUnit = () => {
|
|
|
49
50
|
navigate(Routes.ScriptDetail, {
|
|
50
51
|
id: automateId,
|
|
51
52
|
name: scriptName,
|
|
52
|
-
type:
|
|
53
|
+
type: oldType,
|
|
53
54
|
havePermission: true,
|
|
54
55
|
unit,
|
|
55
56
|
isMultiUnits,
|
|
@@ -77,6 +78,7 @@ const SelectUnit = () => {
|
|
|
77
78
|
unit: selectedItem,
|
|
78
79
|
automateId,
|
|
79
80
|
scriptName,
|
|
81
|
+
oldType,
|
|
80
82
|
}
|
|
81
83
|
);
|
|
82
84
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -155,6 +157,7 @@ const SelectUnit = () => {
|
|
|
155
157
|
keyExtractor={(item) => item.id}
|
|
156
158
|
data={data}
|
|
157
159
|
renderItem={renderItem}
|
|
160
|
+
scrollIndicatorInsets={{ right: 1 }}
|
|
158
161
|
/>
|
|
159
162
|
</View>
|
|
160
163
|
</WrapHeaderScrollable>
|
|
@@ -36,6 +36,7 @@ const SetSchedule = ({ route }) => {
|
|
|
36
36
|
automateId, // wilk=l remove later
|
|
37
37
|
automate = {},
|
|
38
38
|
scriptName,
|
|
39
|
+
oldType,
|
|
39
40
|
} = route.params;
|
|
40
41
|
const { navigate, dispatch, goBack } = useNavigation();
|
|
41
42
|
const [repeat, setRepeat] = useState(automate.repeat || REPEAT_OPTIONS.ONCE);
|
|
@@ -119,7 +120,7 @@ const SetSchedule = ({ route }) => {
|
|
|
119
120
|
navigate(Routes.ScriptDetail, {
|
|
120
121
|
id: automateId,
|
|
121
122
|
name: scriptName,
|
|
122
|
-
type:
|
|
123
|
+
type: oldType,
|
|
123
124
|
havePermission: true,
|
|
124
125
|
unit,
|
|
125
126
|
isMultiUnits,
|
|
@@ -136,7 +137,10 @@ const SetSchedule = ({ route }) => {
|
|
|
136
137
|
<>
|
|
137
138
|
<View style={styles.container}>
|
|
138
139
|
<HeaderCustom isShowClose onClose={onClose} />
|
|
139
|
-
<ScrollView
|
|
140
|
+
<ScrollView
|
|
141
|
+
contentContainerStyle={styles.scollView}
|
|
142
|
+
scrollIndicatorInsets={{ right: 1 }}
|
|
143
|
+
>
|
|
140
144
|
<Text type="H2" bold style={styles.title}>
|
|
141
145
|
{t('set_schedule')}
|
|
142
146
|
</Text>
|
|
@@ -139,6 +139,7 @@ const Shared = () => {
|
|
|
139
139
|
keyExtractor={(item, index) => item.id.toString()}
|
|
140
140
|
ItemSeparatorComponent={() => <View style={styles.seperator} />}
|
|
141
141
|
contentContainerStyle={styles.scrollView}
|
|
142
|
+
scrollIndicatorInsets={{ right: 1 }}
|
|
142
143
|
/>
|
|
143
144
|
) : (
|
|
144
145
|
<FlatList
|
|
@@ -162,6 +163,7 @@ const Shared = () => {
|
|
|
162
163
|
keyExtractor={(item, index) => item.id.toString()}
|
|
163
164
|
ItemSeparatorComponent={() => <View style={styles.seperator} />}
|
|
164
165
|
contentContainerStyle={styles.scrollView}
|
|
166
|
+
scrollIndicatorInsets={{ right: 1 }}
|
|
165
167
|
/>
|
|
166
168
|
)}
|
|
167
169
|
</View>
|
|
@@ -21,10 +21,8 @@ const MemberList = ({ route }) => {
|
|
|
21
21
|
const isFocused = useIsFocused();
|
|
22
22
|
const account = useSCContextSelector((state) => state.auth.account);
|
|
23
23
|
const { unitId, unit } = route.params;
|
|
24
|
-
const { dataMembers, isRefresh, onRefresh, leaveUnit } =
|
|
25
|
-
unitId,
|
|
26
|
-
unit?.user_id
|
|
27
|
-
);
|
|
24
|
+
const { dataMembers, isRefresh, onRefresh, leaveUnit, loading } =
|
|
25
|
+
useDataMember(unitId, unit?.user_id);
|
|
28
26
|
const { isOwner } = useIsOwnerOfUnit(unit?.user_id);
|
|
29
27
|
|
|
30
28
|
const { stateAlertSharingMenu, hideStateAlertSharingMenu, stateLeaveUnit } =
|
|
@@ -67,6 +65,7 @@ const MemberList = ({ route }) => {
|
|
|
67
65
|
/>
|
|
68
66
|
</TouchableOpacity>
|
|
69
67
|
);
|
|
68
|
+
|
|
70
69
|
return (
|
|
71
70
|
<View style={styles.container}>
|
|
72
71
|
<WrapHeaderScrollable
|
|
@@ -77,13 +76,15 @@ const MemberList = ({ route }) => {
|
|
|
77
76
|
headerAniStyle={styles.headerAniStyle}
|
|
78
77
|
styleScrollView={{ backgroundColor: Colors.White }}
|
|
79
78
|
>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
{!loading && (
|
|
80
|
+
<SharingMembers
|
|
81
|
+
testID={TESTID.SHARING_MEMBER}
|
|
82
|
+
dataMember={dataMembers}
|
|
83
|
+
unit={unit}
|
|
84
|
+
ownerId={unit.user_id}
|
|
85
|
+
currentUserId={account.user.id}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
87
88
|
</WrapHeaderScrollable>
|
|
88
89
|
<AlertAction
|
|
89
90
|
visible={stateAlertSharingMenu.visible}
|
|
@@ -225,7 +225,6 @@ const SelectPermission = ({ route }) => {
|
|
|
225
225
|
? member?.phone_number
|
|
226
226
|
: member?.phone_number || '';
|
|
227
227
|
const email = member?.phone_number ? '' : member?.email || '';
|
|
228
|
-
|
|
229
228
|
const { success } = await axiosPost(API.SHARE.SHARE(), {
|
|
230
229
|
phone,
|
|
231
230
|
email,
|
|
@@ -266,6 +265,7 @@ const SelectPermission = ({ route }) => {
|
|
|
266
265
|
/>
|
|
267
266
|
)
|
|
268
267
|
}
|
|
268
|
+
scrollIndicatorInsets={{ right: 1 }}
|
|
269
269
|
/>
|
|
270
270
|
)
|
|
271
271
|
);
|
|
@@ -13,6 +13,7 @@ const useDataMember = (unitId, userUnitId = undefined) => {
|
|
|
13
13
|
const { navigate, goBack } = useNavigation();
|
|
14
14
|
const [dataMembers, setDataMembers] = useState([]);
|
|
15
15
|
const [isRefresh, setRefresh] = useState(false);
|
|
16
|
+
const [loading, setLoading] = useState(true);
|
|
16
17
|
const user = useSCContextSelector((state) => state?.auth?.account?.user);
|
|
17
18
|
const { isOwner } = useIsOwnerOfUnit(userUnitId);
|
|
18
19
|
|
|
@@ -42,6 +43,7 @@ const useDataMember = (unitId, userUnitId = undefined) => {
|
|
|
42
43
|
const arrChanged = changePositionOwnerAndMe(data);
|
|
43
44
|
setDataMembers(arrChanged);
|
|
44
45
|
}
|
|
46
|
+
setLoading(false);
|
|
45
47
|
},
|
|
46
48
|
[changePositionOwnerAndMe]
|
|
47
49
|
);
|
|
@@ -82,6 +84,7 @@ const useDataMember = (unitId, userUnitId = undefined) => {
|
|
|
82
84
|
loadMembers,
|
|
83
85
|
isRefresh,
|
|
84
86
|
onRefresh,
|
|
87
|
+
loading,
|
|
85
88
|
};
|
|
86
89
|
};
|
|
87
90
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Colors } from '../../configs';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import { getBottomSpace } from 'react-native-iphone-x-helper';
|
|
4
|
+
|
|
5
|
+
export default StyleSheet.create({
|
|
6
|
+
container: {
|
|
7
|
+
flex: 1,
|
|
8
|
+
backgroundColor: Colors.White,
|
|
9
|
+
},
|
|
10
|
+
scrollview: {
|
|
11
|
+
flex: 1,
|
|
12
|
+
marginHorizontal: 16,
|
|
13
|
+
},
|
|
14
|
+
wrap: {
|
|
15
|
+
paddingBottom: 100,
|
|
16
|
+
},
|
|
17
|
+
viewBottomFixed: {
|
|
18
|
+
position: 'absolute',
|
|
19
|
+
bottom: 0,
|
|
20
|
+
left: 0,
|
|
21
|
+
right: 0,
|
|
22
|
+
paddingBottom: getBottomSpace() > 0 ? getBottomSpace() : 32,
|
|
23
|
+
paddingTop: 24,
|
|
24
|
+
backgroundColor: Colors.White,
|
|
25
|
+
borderColor: Colors.ShadownTransparent,
|
|
26
|
+
borderTopWidth: 1,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { act, create } from 'react-test-renderer';
|
|
4
|
+
|
|
5
|
+
import { TESTID } from '../../../configs/Constants';
|
|
6
|
+
|
|
7
|
+
import { SCProvider } from '../../../context';
|
|
8
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
9
|
+
import SideMenuDetail from '../index';
|
|
10
|
+
import { API } from '../../../configs';
|
|
11
|
+
import { TouchableOpacity } from 'react-native';
|
|
12
|
+
import { ModalCustom } from '../../../commons/Modal';
|
|
13
|
+
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
14
|
+
|
|
15
|
+
jest.mock('axios');
|
|
16
|
+
|
|
17
|
+
const wrapComponent = (route) => (
|
|
18
|
+
<SCProvider initState={mockSCStore({})}>
|
|
19
|
+
<SideMenuDetail route={route} />
|
|
20
|
+
</SCProvider>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
describe('Test SideMenuDetail', () => {
|
|
24
|
+
let tree;
|
|
25
|
+
const spyToastSuccess = jest.spyOn(ToastBottomHelper, 'success');
|
|
26
|
+
const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
axios.get.mockClear();
|
|
30
|
+
axios.post.mockClear();
|
|
31
|
+
spyToastSuccess.mockReset();
|
|
32
|
+
spyToastSuccess.mockRestore();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const chooseUser = async (instance) => {
|
|
36
|
+
const ChooseField = instance.find(
|
|
37
|
+
(item) =>
|
|
38
|
+
item.props.testID === TESTID.CHOOSE_FIELD &&
|
|
39
|
+
item.type === TouchableOpacity
|
|
40
|
+
);
|
|
41
|
+
await act(async () => {
|
|
42
|
+
await ChooseField.props.onPress();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const popup = instance.find(
|
|
46
|
+
(item) =>
|
|
47
|
+
item.props.testID === TESTID.CHOOSE_POPUP && item.type === ModalCustom
|
|
48
|
+
);
|
|
49
|
+
expect(popup.props.isVisible).toBeTruthy();
|
|
50
|
+
|
|
51
|
+
const button_choose_done = instance.find(
|
|
52
|
+
(el) =>
|
|
53
|
+
el.props.testID === TESTID.CHOOSE_POPUP + TESTID.BOTTOM_VIEW_MAIN &&
|
|
54
|
+
el.type === TouchableOpacity
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
await act(async () => {
|
|
58
|
+
await button_choose_done.props.onPress();
|
|
59
|
+
});
|
|
60
|
+
expect(popup.props.isVisible).toBeFalsy();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const inputPasscode = async (instance) => {
|
|
64
|
+
const inputPasscode = instance.find(
|
|
65
|
+
(item) => item.props.testID === TESTID.PASSCODE_FIELD
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
await act(async () => {
|
|
69
|
+
await inputPasscode.props.onChangeText('123456');
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const onPressSubmitData = async (instance) => {
|
|
74
|
+
const buttonSubmit = instance.find(
|
|
75
|
+
(el) =>
|
|
76
|
+
el.props.testID === TESTID.SUBMIT + TESTID.BOTTOM_VIEW_MAIN &&
|
|
77
|
+
el.type === TouchableOpacity
|
|
78
|
+
);
|
|
79
|
+
await act(async () => {
|
|
80
|
+
await buttonSubmit.props.onPress();
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
test('render SideMenuDetail template choose field', async () => {
|
|
85
|
+
const response_side_menu = {
|
|
86
|
+
status: 200,
|
|
87
|
+
data: {
|
|
88
|
+
id: 1,
|
|
89
|
+
action: '3df74a11-1a95-4a65-8e08-936b88c8b5ca',
|
|
90
|
+
fields: [
|
|
91
|
+
{
|
|
92
|
+
type: 'choose_user',
|
|
93
|
+
key: 'user_id',
|
|
94
|
+
action: {
|
|
95
|
+
type: 'action_zigbee',
|
|
96
|
+
id: 1,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: 'passcode',
|
|
101
|
+
key: 'passcode',
|
|
102
|
+
action: {
|
|
103
|
+
type: 'action_zigbee',
|
|
104
|
+
id: 1,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
type: 'schedule',
|
|
109
|
+
key: 'schedule',
|
|
110
|
+
action: {
|
|
111
|
+
type: 'action_zigbee',
|
|
112
|
+
id: {
|
|
113
|
+
always: 2,
|
|
114
|
+
recurring: 4,
|
|
115
|
+
temporary: 5,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
const response_unit_members = {
|
|
123
|
+
status: 200,
|
|
124
|
+
data: [{ id: 1, name: 'Ken' }],
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
axios.get.mockImplementation((url) => {
|
|
128
|
+
if (url === API.SENSOR.SIDE_MENU_DETAIL(1, 1)) {
|
|
129
|
+
return response_side_menu;
|
|
130
|
+
} else if (url === API.SHARE.UNITS_MEMBERS(1)) {
|
|
131
|
+
return response_unit_members;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
status: 200,
|
|
135
|
+
data: [],
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
axios.post.mockImplementation(() => {
|
|
140
|
+
return {
|
|
141
|
+
status: 200,
|
|
142
|
+
data: [],
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
const route = {
|
|
146
|
+
params: {
|
|
147
|
+
unit: { id: 1 },
|
|
148
|
+
sensor: { id: 1 },
|
|
149
|
+
side_menu: { id: 1, name: 'Setup generate pass code' },
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
await act(async () => {
|
|
153
|
+
tree = await create(wrapComponent(route));
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const instance = tree.root;
|
|
157
|
+
await chooseUser(instance);
|
|
158
|
+
await onPressSubmitData(instance);
|
|
159
|
+
expect(spyToastError).toBeCalled();
|
|
160
|
+
|
|
161
|
+
await inputPasscode(instance);
|
|
162
|
+
await onPressSubmitData(instance);
|
|
163
|
+
expect(spyToastSuccess).toBeCalled();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React, { memo, useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { View, ScrollView } from 'react-native';
|
|
3
|
+
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
4
|
+
|
|
5
|
+
import styles from './SideMenuDetailStyles';
|
|
6
|
+
import { HeaderCustom } from '../../commons/Header';
|
|
7
|
+
import BottomButtonView from '../../commons/BottomButtonView';
|
|
8
|
+
import { axiosGet, axiosPost } from '../../utils/Apis/axios';
|
|
9
|
+
import { API } from '../../configs';
|
|
10
|
+
import PasscodeField from '../../commons/FieldTemplate/PasscodeField';
|
|
11
|
+
import ChooseUserField from '../../commons/FieldTemplate/ChooseUserField';
|
|
12
|
+
import ScheduleField from '../../commons/FieldTemplate/ScheduleField';
|
|
13
|
+
import { TESTID } from '../../configs/Constants';
|
|
14
|
+
import { ToastBottomHelper } from '../../utils/Utils';
|
|
15
|
+
import { useNavigation } from '@react-navigation/native';
|
|
16
|
+
|
|
17
|
+
const SideMenuDetail = memo(({ route }) => {
|
|
18
|
+
const t = useTranslations();
|
|
19
|
+
const { goBack } = useNavigation();
|
|
20
|
+
const { unit, sensor, side_menu } = route?.params || {};
|
|
21
|
+
const [sideMenu, setSideMenu] = useState({});
|
|
22
|
+
const [dataForm, setDataForm] = useState([]);
|
|
23
|
+
|
|
24
|
+
const renderTemplateField = useCallback(
|
|
25
|
+
(item, index) => {
|
|
26
|
+
const keyItem = item.key;
|
|
27
|
+
const dataItem = item;
|
|
28
|
+
switch (item.type) {
|
|
29
|
+
case 'choose_user':
|
|
30
|
+
return (
|
|
31
|
+
<ChooseUserField
|
|
32
|
+
key={index} // Todo avoid waring unique "key" prop
|
|
33
|
+
unit={unit}
|
|
34
|
+
dataItem={dataItem}
|
|
35
|
+
index={index}
|
|
36
|
+
setDataForm={setDataForm}
|
|
37
|
+
dataForm={dataForm}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
case 'passcode':
|
|
41
|
+
return (
|
|
42
|
+
<PasscodeField
|
|
43
|
+
key={index}
|
|
44
|
+
dataItem={dataItem}
|
|
45
|
+
index={index}
|
|
46
|
+
setDataForm={setDataForm}
|
|
47
|
+
dataForm={dataForm}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
case 'schedule':
|
|
51
|
+
return (
|
|
52
|
+
<ScheduleField
|
|
53
|
+
key={index}
|
|
54
|
+
dataItem={dataItem}
|
|
55
|
+
keyItem={keyItem}
|
|
56
|
+
setDataForm={setDataForm}
|
|
57
|
+
dataForm={dataForm}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
default:
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
65
|
+
[unit, dataForm]
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const onPressSubmit = useCallback(async () => {
|
|
69
|
+
const data = {};
|
|
70
|
+
const actionZigbee = [];
|
|
71
|
+
for (let i = 0; i < dataForm.length; i++) {
|
|
72
|
+
if (!dataForm[i].valid) {
|
|
73
|
+
ToastBottomHelper.error(t('invaild_data'));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (dataForm[i].data) {
|
|
77
|
+
data[dataForm[i].key] = dataForm[i].data;
|
|
78
|
+
if (
|
|
79
|
+
dataForm[i].action_zigbee &&
|
|
80
|
+
!actionZigbee.includes(dataForm[i].action_zigbee)
|
|
81
|
+
) {
|
|
82
|
+
actionZigbee.push(dataForm[i].action_zigbee);
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
data[dataForm[i].key] = null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
data.action_zigbee = actionZigbee;
|
|
90
|
+
|
|
91
|
+
const { success } = await axiosPost(API.SENSOR.QUICK_ACTION(sensor.id), {
|
|
92
|
+
key: sideMenu.action,
|
|
93
|
+
data,
|
|
94
|
+
source: 'smart_lock',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (success) {
|
|
98
|
+
ToastBottomHelper.success(t('activated_successfully'));
|
|
99
|
+
goBack();
|
|
100
|
+
} else {
|
|
101
|
+
ToastBottomHelper.error(t('activation_failed'));
|
|
102
|
+
}
|
|
103
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
104
|
+
}, [dataForm]);
|
|
105
|
+
|
|
106
|
+
const fetchSideMenuDetail = useCallback(async () => {
|
|
107
|
+
const { success, data } = await axiosGet(
|
|
108
|
+
API.SENSOR.SIDE_MENU_DETAIL(sensor.id, side_menu.id)
|
|
109
|
+
);
|
|
110
|
+
if (success) {
|
|
111
|
+
setSideMenu(data);
|
|
112
|
+
setDataForm(data.fields);
|
|
113
|
+
}
|
|
114
|
+
}, [sensor.id, side_menu.id]);
|
|
115
|
+
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
fetchSideMenuDetail();
|
|
118
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
119
|
+
}, []);
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<View style={styles.container}>
|
|
123
|
+
<HeaderCustom
|
|
124
|
+
title={side_menu.name}
|
|
125
|
+
titleStyle={styles.title}
|
|
126
|
+
isShowSeparator
|
|
127
|
+
/>
|
|
128
|
+
<ScrollView
|
|
129
|
+
showsVerticalScrollIndicator={false}
|
|
130
|
+
showsHorizontalScrollIndicator={false}
|
|
131
|
+
style={styles.scrollview}
|
|
132
|
+
>
|
|
133
|
+
<View style={styles.wrap}>
|
|
134
|
+
{dataForm.map((item, index) => {
|
|
135
|
+
return renderTemplateField(item, index);
|
|
136
|
+
})}
|
|
137
|
+
</View>
|
|
138
|
+
</ScrollView>
|
|
139
|
+
<BottomButtonView
|
|
140
|
+
style={styles.viewBottomFixed}
|
|
141
|
+
testIDPrefix={TESTID.SUBMIT}
|
|
142
|
+
mainTitle={t('done')}
|
|
143
|
+
onPressMain={onPressSubmit}
|
|
144
|
+
/>
|
|
145
|
+
</View>
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
export default SideMenuDetail;
|
|
@@ -36,7 +36,7 @@ const SelectDeviceType = memo(({ route }) => {
|
|
|
36
36
|
return (
|
|
37
37
|
<View style={styles.container}>
|
|
38
38
|
<HeaderCustom title={t('select_brand')} />
|
|
39
|
-
<ScrollView>
|
|
39
|
+
<ScrollView scrollIndicatorInsets={{ right: 1 }}>
|
|
40
40
|
<View style={styles.listBrands}>
|
|
41
41
|
{!!deviceBrand &&
|
|
42
42
|
deviceBrand.map((item, index) => {
|