@eohjsc/react-native-smart-city 0.2.59 → 0.2.63
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 +115 -68
- package/assets/images/Map/MarkerGeolocation.svg +4 -0
- package/package.json +3 -3
- package/src/commons/ActionGroup/CurtainButtonTemplate.js +10 -2
- package/src/commons/ActionGroup/__test__/CurtainButtonTemplate.test.js +1 -1
- package/src/commons/ActionGroup/__test__/MenuActionAddSchedule.test.js +71 -0
- package/src/commons/ActionGroup/hooks/AccessScheduleDetailStyles.js +41 -0
- package/src/commons/ActionGroup/hooks/MenuActionAddSchedule.js +110 -0
- package/src/commons/ActionGroup/hooks/MenuActionAddScheduleStyle.js +69 -0
- package/src/commons/ActionGroup/hooks/RecurringDetail.js +97 -0
- package/src/commons/DateTimeRangeChange/DateTimeButton.js +7 -2
- package/src/commons/Device/HistoryChart.js +80 -81
- package/src/commons/Device/HorizontalBarChart.js +48 -31
- package/src/commons/Device/LinearChart.js +28 -1
- package/src/commons/Form/CurrencyInput.js +1 -0
- package/src/commons/FourButtonFilterHistory/__test__/FourButtonFilterHistory.test.js +48 -0
- package/src/commons/FourButtonFilterHistory/index.js +72 -0
- package/src/commons/FourButtonFilterHistory/styles.js +22 -0
- package/src/commons/ImagePicker/index.js +27 -33
- package/src/commons/MediaPlayerDetail/Styles/MediaPlayerDetailStyles.js +11 -1
- package/src/commons/MediaPlayerDetail/index.js +14 -5
- package/src/commons/SubUnit/OneTap/OneTapStyles.js +20 -1
- package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +151 -40
- package/src/commons/SubUnit/OneTap/index.js +64 -12
- package/src/commons/UnitSummary/AirQuality/index.js +9 -7
- package/src/commons/UnitSummary/ConfigHistoryChart.js +2 -1
- package/src/configs/API.js +3 -0
- package/src/configs/Constants.js +15 -0
- package/src/iot/RemoteControl/Bluetooth.js +6 -3
- package/src/iot/RemoteControl/GoogleHome.js +6 -3
- package/src/iot/RemoteControl/Internet.js +1 -0
- package/src/iot/RemoteControl/LG.js +2 -1
- package/src/iot/RemoteControl/index.js +13 -6
- package/src/navigations/SharedStack.js +11 -9
- package/src/navigations/UnitStack.js +26 -2
- package/src/screens/ActivityLog/ItemLog.js +3 -3
- package/src/screens/ActivityLog/__test__/ItemLog.test.js +5 -2
- package/src/screens/ActivityLog/hooks/index.js +2 -1
- package/src/screens/ActivityLog/index.js +0 -1
- package/src/screens/AddLocationMaps/index.js +4 -2
- package/src/screens/AddNewAction/SelectSensorDevices.js +18 -11
- package/src/screens/AddNewAction/Styles/SelectSensorDevicesStyles.js +5 -1
- package/src/screens/AddNewAction/__test__/SelectSensorDevices.test.js +6 -1
- package/src/screens/Automate/MultiUnits.js +7 -4
- package/src/screens/Automate/__test__/MultiUnits.test.js +1 -1
- package/src/screens/Automate/__test__/index.test.js +12 -0
- package/src/screens/ConfirmUnitDeletion/__test__/ConfirmUnitDeletion.test.js +61 -0
- package/src/screens/ConfirmUnitDeletion/index.js +64 -0
- package/src/screens/ConfirmUnitDeletion/styles.js +37 -0
- package/src/screens/Device/__test__/detail.test.js +3 -2
- package/src/screens/Device/detail.js +48 -15
- package/src/screens/Device/hooks/useDisconnectedDevice.js +2 -1
- package/src/screens/Device/styles.js +3 -3
- package/src/screens/EmergencySetting/__test__/DropDownItem.test.js +59 -0
- package/src/screens/EmergencySetting/__test__/index.test.js +27 -0
- package/src/screens/EmergencySetting/components/DropDownItem.js +54 -0
- package/src/screens/EmergencySetting/index.js +92 -0
- package/src/screens/EmergencySetting/styles/DropDownItem.js +38 -0
- package/src/screens/EmergencySetting/styles.js +25 -0
- package/src/screens/MoveToAnotherSubUnit/__test__/index.test.js +126 -0
- package/src/screens/MoveToAnotherSubUnit/index.js +88 -0
- package/src/screens/MoveToAnotherSubUnit/styles/MoveToAnotherSubUnitStyles.js +50 -0
- package/src/screens/ScriptDetail/Styles/indexStyles.js +0 -1
- package/src/screens/ScriptDetail/index.js +1 -0
- package/src/screens/SubUnit/AddSubUnit.js +3 -3
- package/src/screens/SubUnit/AddSubUnitStyles.js +0 -2
- package/src/screens/SubUnit/EditSubUnit.js +16 -7
- package/src/screens/SubUnit/EditSubUnitStyles.js +2 -3
- package/src/screens/SubUnit/__test__/EditSubUnit.test.js +2 -2
- package/src/screens/TDSGuide/index.js +1 -1
- package/src/screens/Unit/ChooseLocation.js +3 -7
- package/src/screens/Unit/ChooseLocationStyles.js +5 -8
- package/src/screens/Unit/Detail.js +16 -6
- package/src/screens/Unit/ManageUnit.js +20 -26
- package/src/screens/Unit/SmartAccount.js +25 -41
- package/src/screens/Unit/SmartAccountItem.js +2 -1
- package/src/screens/Unit/SmartAccountStyles.js +0 -1
- package/src/screens/Unit/__test__/ManageUnit.test.js +0 -6
- package/src/screens/Unit/__test__/SmartAccount.test.js +24 -0
- package/src/screens/Unit/__test__/SmartAccountItem.test.js +72 -0
- package/src/screens/UnitSummary/components/3PPowerConsumption/index.js +58 -59
- package/src/screens/UnitSummary/components/PowerConsumption/index.js +26 -22
- package/src/screens/UnitSummary/components/Temperature/ItemTemperature/index.js +2 -2
- package/src/screens/UnitSummary/components/Temperature/index.js +15 -14
- package/src/screens/UnitSummary/components/UvIndex/index.js +6 -5
- package/src/screens/UnitSummary/components/WaterQuality/index.js +9 -7
- package/src/screens/UnitSummary/index.js +11 -7
- package/src/screens/WaterQualityGuide/index.js +1 -0
- package/src/utils/Apis/axios.js +4 -4
- package/src/utils/I18n/translations/en.json +20 -2
- package/src/utils/I18n/translations/vi.json +21 -2
- package/src/utils/Route/index.js +3 -0
- package/src/utils/Utils.js +4 -0
- package/src/commons/ThreeButtonHistory/CalendarHeader.js +0 -35
- package/src/commons/ThreeButtonHistory/CalendarHeaderStyles.js +0 -17
- package/src/commons/ThreeButtonHistory/SelectMonth.js +0 -53
- package/src/commons/ThreeButtonHistory/SelectMonthStyles.js +0 -29
- package/src/commons/ThreeButtonHistory/__test__/SelectMonth.test.js +0 -37
- package/src/commons/ThreeButtonHistory/__test__/ThreeButtonHistory.test.js +0 -240
- package/src/commons/ThreeButtonHistory/index.js +0 -310
- package/src/commons/ThreeButtonHistory/styles.js +0 -65
|
@@ -59,8 +59,11 @@ const DeviceDetail = ({ route }) => {
|
|
|
59
59
|
internet: {},
|
|
60
60
|
});
|
|
61
61
|
// eslint-disable-next-line no-unused-vars
|
|
62
|
-
const [loading, setLoading] = useState(
|
|
63
|
-
|
|
62
|
+
const [loading, setLoading] = useState({
|
|
63
|
+
isConnected: true,
|
|
64
|
+
displayTemplate: true,
|
|
65
|
+
});
|
|
66
|
+
const [isConnected, setConnected] = useState(false);
|
|
64
67
|
const [lastUpdated, setLastUpdated] = useState(null);
|
|
65
68
|
const [lastEvent, setLastEvent] = useState({ id: 0, reportedAt: 0 });
|
|
66
69
|
const [maxValue, setMaxValue] = useState(60);
|
|
@@ -162,7 +165,7 @@ const DeviceDetail = ({ route }) => {
|
|
|
162
165
|
}
|
|
163
166
|
}
|
|
164
167
|
}
|
|
165
|
-
setLoading(false);
|
|
168
|
+
setLoading((preState) => ({ ...preState, displayTemplate: false }));
|
|
166
169
|
|
|
167
170
|
const controlResult = await axiosGet(
|
|
168
171
|
API.SENSOR.REMOTE_CONTROL_OPTIONS(sensor.id),
|
|
@@ -211,9 +214,10 @@ const DeviceDetail = ({ route }) => {
|
|
|
211
214
|
data: {
|
|
212
215
|
id: sensor.id,
|
|
213
216
|
type: 'action',
|
|
217
|
+
share: unit,
|
|
214
218
|
filterEnabled: {
|
|
215
|
-
date:
|
|
216
|
-
user:
|
|
219
|
+
date: true,
|
|
220
|
+
user: Boolean(unit.id),
|
|
217
221
|
},
|
|
218
222
|
},
|
|
219
223
|
text: t('activity_log'),
|
|
@@ -234,6 +238,11 @@ const DeviceDetail = ({ route }) => {
|
|
|
234
238
|
data: { unit, sensor },
|
|
235
239
|
text: t('manage_access'),
|
|
236
240
|
});
|
|
241
|
+
menuItems.push({
|
|
242
|
+
text: t('move_to_another_sub_unit'),
|
|
243
|
+
route: Routes.MoveToAnotherSubUnit,
|
|
244
|
+
data: { unit, sensor, station },
|
|
245
|
+
});
|
|
237
246
|
}
|
|
238
247
|
if (isShowSetupEmergencyContact) {
|
|
239
248
|
menuItems.push({
|
|
@@ -270,15 +279,15 @@ const DeviceDetail = ({ route }) => {
|
|
|
270
279
|
return [...menuItems];
|
|
271
280
|
}, [
|
|
272
281
|
display.items,
|
|
282
|
+
isOwner,
|
|
283
|
+
isShowSetupEmergencyContact,
|
|
273
284
|
t,
|
|
274
285
|
isFavourite,
|
|
275
|
-
emergencyDeviceId,
|
|
276
|
-
isShowSetupEmergencyContact,
|
|
277
286
|
sensor,
|
|
278
|
-
setSensorName,
|
|
279
287
|
sensorName,
|
|
280
|
-
isOwner,
|
|
281
288
|
unit,
|
|
289
|
+
station,
|
|
290
|
+
emergencyDeviceId,
|
|
282
291
|
addToFavorites,
|
|
283
292
|
removeFromFavorites,
|
|
284
293
|
]);
|
|
@@ -354,11 +363,14 @@ const DeviceDetail = ({ route }) => {
|
|
|
354
363
|
transformDatetime(data, ['last_updated']);
|
|
355
364
|
setLastUpdated(data.last_updated);
|
|
356
365
|
}
|
|
366
|
+
setLoading((preState) => ({ ...preState, isConnected: false }));
|
|
357
367
|
};
|
|
358
368
|
if (sensor.is_managed_by_backend && !sensor.is_other_device) {
|
|
359
369
|
const updateInterval = setInterval(() => fetchValues(), 5000);
|
|
360
370
|
fetchValues();
|
|
361
371
|
return () => clearInterval(updateInterval);
|
|
372
|
+
} else {
|
|
373
|
+
setLoading((preState) => ({ ...preState, isConnected: false }));
|
|
362
374
|
}
|
|
363
375
|
}, [sensor, display]);
|
|
364
376
|
|
|
@@ -444,7 +456,7 @@ const DeviceDetail = ({ route }) => {
|
|
|
444
456
|
`@CACHE_REQUEST_${API.SENSOR.REMOTE_CONTROL_OPTIONS(sensor.id)}`
|
|
445
457
|
);
|
|
446
458
|
controlOptionData && setControlOptions(JSON.parse(controlOptionData));
|
|
447
|
-
setLoading(false);
|
|
459
|
+
setLoading((preState) => ({ ...preState, displayTemplate: false }));
|
|
448
460
|
};
|
|
449
461
|
|
|
450
462
|
const onItemMenuClicked = (item) => {
|
|
@@ -469,6 +481,10 @@ const DeviceDetail = ({ route }) => {
|
|
|
469
481
|
showPopoverWithRef(refMenuAction);
|
|
470
482
|
}, [showPopoverWithRef, refMenuAction]);
|
|
471
483
|
|
|
484
|
+
const onPressSetting = useCallback(() => {
|
|
485
|
+
navigation.navigate(Routes.EmergencySetting);
|
|
486
|
+
}, [navigation]);
|
|
487
|
+
|
|
472
488
|
const HeaderRight = useMemo(
|
|
473
489
|
() => (
|
|
474
490
|
<View style={styles.headerRight}>
|
|
@@ -483,6 +499,13 @@ const DeviceDetail = ({ route }) => {
|
|
|
483
499
|
<IconOutline name="star" size={25} />
|
|
484
500
|
)}
|
|
485
501
|
</TouchableOpacity>
|
|
502
|
+
|
|
503
|
+
{isShowSetupEmergencyContact && (
|
|
504
|
+
<TouchableOpacity style={styles.button} onPress={onPressSetting}>
|
|
505
|
+
<Icon name="setting" size={25} color={Colors.Black} />
|
|
506
|
+
</TouchableOpacity>
|
|
507
|
+
)}
|
|
508
|
+
|
|
486
509
|
<TouchableOpacity
|
|
487
510
|
style={styles.button}
|
|
488
511
|
onPress={handleShowMenuAction}
|
|
@@ -493,7 +516,14 @@ const DeviceDetail = ({ route }) => {
|
|
|
493
516
|
</TouchableOpacity>
|
|
494
517
|
</View>
|
|
495
518
|
),
|
|
496
|
-
[
|
|
519
|
+
[
|
|
520
|
+
isFavourite,
|
|
521
|
+
removeFromFavorites,
|
|
522
|
+
addToFavorites,
|
|
523
|
+
isShowSetupEmergencyContact,
|
|
524
|
+
onPressSetting,
|
|
525
|
+
handleShowMenuAction,
|
|
526
|
+
]
|
|
497
527
|
);
|
|
498
528
|
|
|
499
529
|
return (
|
|
@@ -505,15 +535,18 @@ const DeviceDetail = ({ route }) => {
|
|
|
505
535
|
onRefresh={onRefresh}
|
|
506
536
|
>
|
|
507
537
|
<View style={styles.wrapTemplate}>
|
|
508
|
-
{
|
|
538
|
+
{loading.displayTemplate === false &&
|
|
539
|
+
loading.isConnected === false &&
|
|
540
|
+
netInfo.isConnected !== null &&
|
|
541
|
+
renderSensorConnected()}
|
|
509
542
|
</View>
|
|
510
543
|
{isShowSetupEmergencyContact && canManageSubUnit && (
|
|
511
544
|
<BottomButtonView
|
|
512
545
|
style={styles.bottomButtonEmergencyContact}
|
|
513
|
-
mainIcon={<Icon name="
|
|
514
|
-
mainTitle={t('
|
|
546
|
+
mainIcon={<Icon name="plus" size={16} color={Colors.Primary} />}
|
|
547
|
+
mainTitle={t('setup_my_emergency_contact')}
|
|
515
548
|
onPressMain={onSetupContacts}
|
|
516
|
-
typeMain="
|
|
549
|
+
typeMain="primaryBorder"
|
|
517
550
|
semiboldMain={false}
|
|
518
551
|
/>
|
|
519
552
|
)}
|
|
@@ -39,7 +39,8 @@ export const useDisconnectedDevice = (sensorName, isDeviceHasBle) => {
|
|
|
39
39
|
|
|
40
40
|
const checkNetWorkConnect = useCallback(
|
|
41
41
|
async (isHavingInternet, isBtEnabled) => {
|
|
42
|
-
if (
|
|
42
|
+
if (isHavingInternet === false && isDeviceHasBle) {
|
|
43
|
+
// TODO avoid case first render isHavingInternet == null
|
|
43
44
|
if (isBtEnabled === true) {
|
|
44
45
|
ToastBottomHelper.info(
|
|
45
46
|
t('your_internet_is_disconnected', { name: sensorName }),
|
|
@@ -32,7 +32,7 @@ export default StyleSheet.create({
|
|
|
32
32
|
},
|
|
33
33
|
bottomButtonEmergencyContact: {
|
|
34
34
|
marginHorizontal: 16,
|
|
35
|
-
marginBottom:
|
|
35
|
+
marginBottom: 40,
|
|
36
36
|
},
|
|
37
37
|
locationName: {
|
|
38
38
|
flex: 1,
|
|
@@ -77,10 +77,10 @@ export default StyleSheet.create({
|
|
|
77
77
|
buttonStar: {
|
|
78
78
|
justifyContent: 'center',
|
|
79
79
|
alignItems: 'center',
|
|
80
|
+
marginRight: 14,
|
|
80
81
|
},
|
|
81
82
|
button: {
|
|
82
|
-
|
|
83
|
-
height: 40,
|
|
83
|
+
marginRight: 10,
|
|
84
84
|
justifyContent: 'center',
|
|
85
85
|
alignItems: 'center',
|
|
86
86
|
},
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { create, act } from 'react-test-renderer';
|
|
3
|
+
import { TouchableOpacity } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import { SCProvider } from '../../../context';
|
|
6
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
7
|
+
import DropDownItem from '../components/DropDownItem';
|
|
8
|
+
import Text from '../../../commons/Text';
|
|
9
|
+
import { TESTID } from '../../../configs/Constants';
|
|
10
|
+
|
|
11
|
+
const mockonOpen = jest.fn();
|
|
12
|
+
|
|
13
|
+
const wrapComponent = (props) => (
|
|
14
|
+
<SCProvider initState={mockSCStore({})}>
|
|
15
|
+
<DropDownItem {...props} />
|
|
16
|
+
</SCProvider>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
describe('Test DropDownItem', () => {
|
|
20
|
+
let tree;
|
|
21
|
+
|
|
22
|
+
test('test render DropDownItem', () => {
|
|
23
|
+
const props = {
|
|
24
|
+
label: 'mode',
|
|
25
|
+
data: [{ label: 'Stop (0)', value: 'stop' }],
|
|
26
|
+
isOpen: true,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
act(() => {
|
|
30
|
+
tree = create(wrapComponent(props));
|
|
31
|
+
});
|
|
32
|
+
const instance = tree.root;
|
|
33
|
+
const dropDownPicker = instance.findAll(
|
|
34
|
+
(el) =>
|
|
35
|
+
el.props.testID === TESTID.DROP_DOWN_PICKER_ITEM && el.type === Text
|
|
36
|
+
);
|
|
37
|
+
expect(dropDownPicker).toHaveLength(1);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('test onPress DropDown', () => {
|
|
41
|
+
const props = {
|
|
42
|
+
label: 'mode',
|
|
43
|
+
data: [{ label: 'Stop (0)', value: 'stop' }],
|
|
44
|
+
onOpen: mockonOpen,
|
|
45
|
+
index: 0,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
act(() => {
|
|
49
|
+
tree = create(wrapComponent(props));
|
|
50
|
+
});
|
|
51
|
+
const instance = tree.root;
|
|
52
|
+
const touchableOpacity = instance.findByType(TouchableOpacity);
|
|
53
|
+
act(() => {
|
|
54
|
+
touchableOpacity.props.onPress();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(mockonOpen).toHaveBeenCalledWith(props.index);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { create, act } from 'react-test-renderer';
|
|
3
|
+
|
|
4
|
+
import { SCProvider } from '../../../context';
|
|
5
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
6
|
+
import EmergencySetting from '..';
|
|
7
|
+
import DropDownItem from '../components/DropDownItem';
|
|
8
|
+
|
|
9
|
+
const wrapComponent = () => (
|
|
10
|
+
<SCProvider initState={mockSCStore({})}>
|
|
11
|
+
<EmergencySetting />
|
|
12
|
+
</SCProvider>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
describe('test EmergencySetting', () => {
|
|
16
|
+
let tree;
|
|
17
|
+
|
|
18
|
+
test('test render EmergencySetting', () => {
|
|
19
|
+
act(() => {
|
|
20
|
+
tree = create(wrapComponent());
|
|
21
|
+
});
|
|
22
|
+
const instance = tree.root;
|
|
23
|
+
const dropDownItem = instance.findAllByType(DropDownItem);
|
|
24
|
+
|
|
25
|
+
expect(dropDownItem.length).toEqual(3);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { View, TouchableOpacity } from 'react-native';
|
|
3
|
+
import { IconFill } from '@ant-design/icons-react-native';
|
|
4
|
+
|
|
5
|
+
import Text from '../../../commons/Text';
|
|
6
|
+
import styles from '../styles/DropDownItem';
|
|
7
|
+
import { Colors } from '../../../configs';
|
|
8
|
+
import { TESTID } from '../../../configs/Constants';
|
|
9
|
+
|
|
10
|
+
const DropDownItem = ({ label, data, onSelectItem, isOpen, onOpen, index }) => {
|
|
11
|
+
const [selecteValue, setSelectValue] = useState(data[0] || {});
|
|
12
|
+
|
|
13
|
+
const handleSelectItem = useCallback(
|
|
14
|
+
(item) => () => {
|
|
15
|
+
setSelectValue(item);
|
|
16
|
+
onSelectItem && onSelectItem();
|
|
17
|
+
},
|
|
18
|
+
[onSelectItem]
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const handleOnOpen = useCallback(() => {
|
|
22
|
+
onOpen && onOpen(index);
|
|
23
|
+
}, [index, onOpen]);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
// eslint-disable-next-line react-native/no-inline-styles
|
|
27
|
+
<View style={[styles.wrap, { zIndex: isOpen ? 1 : 0 }]}>
|
|
28
|
+
{label && <Text>{label}</Text>}
|
|
29
|
+
<View style={styles.dropDownContainer}>
|
|
30
|
+
<TouchableOpacity style={styles.dropDownStyle} onPress={handleOnOpen}>
|
|
31
|
+
<Text>{selecteValue?.label}</Text>
|
|
32
|
+
<IconFill name="caret-down" size={18} color={Colors.Gray8} />
|
|
33
|
+
</TouchableOpacity>
|
|
34
|
+
|
|
35
|
+
{isOpen && (
|
|
36
|
+
<View style={[styles.dropDownItem]}>
|
|
37
|
+
{data.map((item, index) => (
|
|
38
|
+
<Text
|
|
39
|
+
style={styles.dropDownText}
|
|
40
|
+
onPress={handleSelectItem(item)}
|
|
41
|
+
key={index}
|
|
42
|
+
testID={TESTID.DROP_DOWN_PICKER_ITEM}
|
|
43
|
+
>
|
|
44
|
+
{item?.label}
|
|
45
|
+
</Text>
|
|
46
|
+
))}
|
|
47
|
+
</View>
|
|
48
|
+
)}
|
|
49
|
+
</View>
|
|
50
|
+
</View>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default DropDownItem;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import React, { useState, useCallback, useMemo } from 'react';
|
|
2
|
+
import { View, ScrollView } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
5
|
+
|
|
6
|
+
import { HeaderCustom } from '../../commons/Header';
|
|
7
|
+
import DropDownItem from './components/DropDownItem';
|
|
8
|
+
import _TextInput from '../../commons/Form/TextInput';
|
|
9
|
+
|
|
10
|
+
import styles from './styles';
|
|
11
|
+
import Text from '../../commons/Text';
|
|
12
|
+
|
|
13
|
+
const EmergencySetting = () => {
|
|
14
|
+
const t = useTranslations();
|
|
15
|
+
const [openedDropdown, setOpenedDropdown] = useState(null);
|
|
16
|
+
const [duration, setDuration] = useState('');
|
|
17
|
+
|
|
18
|
+
const onChangeDuration = useCallback((text) => {
|
|
19
|
+
setDuration(text);
|
|
20
|
+
}, []);
|
|
21
|
+
|
|
22
|
+
const listData = useMemo(() => {
|
|
23
|
+
return [
|
|
24
|
+
{
|
|
25
|
+
label: t('mode'),
|
|
26
|
+
data: [
|
|
27
|
+
{ label: 'Stop (0)', value: 'stop' },
|
|
28
|
+
{ label: 'Stop (1)', value: 'stop' },
|
|
29
|
+
{ label: 'Stop (2)', value: 'stop' },
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: t('level'),
|
|
34
|
+
data: [
|
|
35
|
+
{ label: 'Low', value: 'low' },
|
|
36
|
+
{ label: 'High', value: 'High' },
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: t('strobe'),
|
|
41
|
+
data: [
|
|
42
|
+
{ label: 'True', value: 'true' },
|
|
43
|
+
{ label: 'False', value: 'true' },
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
}, [t]);
|
|
48
|
+
|
|
49
|
+
const handleOnOpen = useCallback((index) => {
|
|
50
|
+
setOpenedDropdown((oldIndex) => {
|
|
51
|
+
if (index === oldIndex) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return index;
|
|
55
|
+
});
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
const handleOnSelectItem = useCallback(() => {
|
|
59
|
+
setOpenedDropdown(null);
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<View style={styles.wrap}>
|
|
64
|
+
<HeaderCustom title={t('setting')} isShowSeparator />
|
|
65
|
+
|
|
66
|
+
<ScrollView contentContainerStyle={styles.contentContainerStyle}>
|
|
67
|
+
{listData.map((item, index) => (
|
|
68
|
+
<DropDownItem
|
|
69
|
+
{...item}
|
|
70
|
+
key={index}
|
|
71
|
+
index={index}
|
|
72
|
+
onOpen={handleOnOpen}
|
|
73
|
+
isOpen={openedDropdown === index}
|
|
74
|
+
onSelectItem={handleOnSelectItem}
|
|
75
|
+
/>
|
|
76
|
+
))}
|
|
77
|
+
|
|
78
|
+
<Text style={styles.duration}>{t('duration')}</Text>
|
|
79
|
+
<_TextInput
|
|
80
|
+
wrapStyle={styles.wrapInput}
|
|
81
|
+
textInputStyle={styles.textInputStyle}
|
|
82
|
+
keyboardType="numeric"
|
|
83
|
+
onChange={onChangeDuration}
|
|
84
|
+
value={duration}
|
|
85
|
+
placeholder="0"
|
|
86
|
+
/>
|
|
87
|
+
</ScrollView>
|
|
88
|
+
</View>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default EmergencySetting;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Colors } from '../../../configs';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
wrap: {
|
|
6
|
+
paddingHorizontal: 16,
|
|
7
|
+
paddingTop: 16,
|
|
8
|
+
},
|
|
9
|
+
dropDownContainer: {
|
|
10
|
+
marginTop: 8,
|
|
11
|
+
},
|
|
12
|
+
dropDownStyle: {
|
|
13
|
+
flexDirection: 'row',
|
|
14
|
+
justifyContent: 'space-between',
|
|
15
|
+
alignItems: 'center',
|
|
16
|
+
borderColor: Colors.Gray5,
|
|
17
|
+
borderRadius: 2,
|
|
18
|
+
borderWidth: 1,
|
|
19
|
+
paddingHorizontal: 16,
|
|
20
|
+
paddingVertical: 8,
|
|
21
|
+
},
|
|
22
|
+
dropDownItem: {
|
|
23
|
+
borderColor: Colors.Gray5,
|
|
24
|
+
borderRadius: 2,
|
|
25
|
+
borderTopWidth: 0,
|
|
26
|
+
borderWidth: 1,
|
|
27
|
+
paddingHorizontal: 16,
|
|
28
|
+
paddingBottom: 8,
|
|
29
|
+
backgroundColor: Colors.White,
|
|
30
|
+
position: 'absolute',
|
|
31
|
+
width: '100%',
|
|
32
|
+
top: 44,
|
|
33
|
+
left: 0,
|
|
34
|
+
},
|
|
35
|
+
dropDownText: {
|
|
36
|
+
marginTop: 8,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Colors } from '../../configs';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
wrap: {
|
|
6
|
+
flex: 1,
|
|
7
|
+
backgroundColor: Colors.White,
|
|
8
|
+
},
|
|
9
|
+
contentContainerStyle: {
|
|
10
|
+
flexGrow: 1,
|
|
11
|
+
},
|
|
12
|
+
wrapInput: {
|
|
13
|
+
paddingHorizontal: 16,
|
|
14
|
+
marginTop: 0,
|
|
15
|
+
},
|
|
16
|
+
textInputStyle: {
|
|
17
|
+
paddingTop: 7,
|
|
18
|
+
paddingBottom: 7,
|
|
19
|
+
},
|
|
20
|
+
duration: {
|
|
21
|
+
marginTop: 14,
|
|
22
|
+
marginBottom: 10,
|
|
23
|
+
marginLeft: 16,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { create } from 'react-test-renderer';
|
|
4
|
+
import { act } from '@testing-library/react-hooks';
|
|
5
|
+
|
|
6
|
+
import { TESTID } from '../../../configs/Constants';
|
|
7
|
+
import { API } from '../../../configs';
|
|
8
|
+
import MoveToAnotherSubUnit from '../';
|
|
9
|
+
import { SCProvider } from '../../../context';
|
|
10
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
11
|
+
import BottomButtonView from '../../../commons/BottomButtonView';
|
|
12
|
+
|
|
13
|
+
jest.mock('axios');
|
|
14
|
+
jest.mock('react', () => {
|
|
15
|
+
return {
|
|
16
|
+
...jest.requireActual('react'),
|
|
17
|
+
memo: (x) => x,
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const wrapComponent = (route) => (
|
|
22
|
+
<SCProvider initState={mockSCStore({})}>
|
|
23
|
+
<MoveToAnotherSubUnit route={route} />
|
|
24
|
+
</SCProvider>
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
describe('Test Render ListSubUnit', () => {
|
|
28
|
+
let tree;
|
|
29
|
+
let route = {
|
|
30
|
+
params: {
|
|
31
|
+
unit: {
|
|
32
|
+
id: 200,
|
|
33
|
+
stations: [
|
|
34
|
+
{
|
|
35
|
+
id: 1,
|
|
36
|
+
name: 'Favourite',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 2,
|
|
40
|
+
name: 'Sceriano',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 3,
|
|
44
|
+
name: 'Room A',
|
|
45
|
+
sensors: [
|
|
46
|
+
{
|
|
47
|
+
action: null,
|
|
48
|
+
action2: null,
|
|
49
|
+
chip_id: 40,
|
|
50
|
+
description: null,
|
|
51
|
+
icon: '',
|
|
52
|
+
icon_kit: '',
|
|
53
|
+
id: 73,
|
|
54
|
+
is_managed_by_backend: true,
|
|
55
|
+
is_other_device: false,
|
|
56
|
+
name: 'Multi-Air Quality',
|
|
57
|
+
quick_action: null,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 4,
|
|
63
|
+
name: 'Room 2',
|
|
64
|
+
sensors: [],
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
station: {
|
|
69
|
+
id: 3,
|
|
70
|
+
name: 'Room 1',
|
|
71
|
+
},
|
|
72
|
+
sensor: {
|
|
73
|
+
action: null,
|
|
74
|
+
action2: null,
|
|
75
|
+
chip_id: 40,
|
|
76
|
+
description: null,
|
|
77
|
+
icon: '',
|
|
78
|
+
icon_kit: '',
|
|
79
|
+
id: 73,
|
|
80
|
+
is_managed_by_backend: true,
|
|
81
|
+
is_other_device: false,
|
|
82
|
+
name: 'Multi-Air Quality',
|
|
83
|
+
quick_action: null,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
it('render ListSubUnit', async () => {
|
|
89
|
+
await act(async () => {
|
|
90
|
+
tree = await create(wrapComponent(route));
|
|
91
|
+
});
|
|
92
|
+
const instance = tree.root;
|
|
93
|
+
const rowSubUnit = instance.findAll(
|
|
94
|
+
(el) => el.props.testID === TESTID.ROW_SUB_UNIT
|
|
95
|
+
);
|
|
96
|
+
expect(rowSubUnit).toHaveLength(2);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('test move sensor', async () => {
|
|
100
|
+
await act(async () => {
|
|
101
|
+
tree = await create(wrapComponent(route));
|
|
102
|
+
});
|
|
103
|
+
const instance = tree.root;
|
|
104
|
+
const bottomButtonView = instance.findByType(BottomButtonView);
|
|
105
|
+
const rowSubUnit = instance.findAll(
|
|
106
|
+
(el) => el.props.testID === TESTID.ROW_SUB_UNIT
|
|
107
|
+
);
|
|
108
|
+
act(() => {
|
|
109
|
+
rowSubUnit[1].props.onSelect({
|
|
110
|
+
id: 4,
|
|
111
|
+
name: 'Room 2',
|
|
112
|
+
sensors: [],
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
act(() => {
|
|
116
|
+
bottomButtonView.props.onPressMain();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(axios.patch).toHaveBeenCalledWith(
|
|
120
|
+
API.SENSOR.CHANGE_SUB_UNIT(200, 3, 73),
|
|
121
|
+
{
|
|
122
|
+
station_id: 4,
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React, { useState, useCallback, memo, useMemo } from 'react';
|
|
2
|
+
import { View, ScrollView, TouchableOpacity } from 'react-native';
|
|
3
|
+
import { useNavigation } from '@react-navigation/native';
|
|
4
|
+
|
|
5
|
+
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
6
|
+
import { HeaderCustom } from '../../commons/Header';
|
|
7
|
+
import styles from './styles/MoveToAnotherSubUnitStyles';
|
|
8
|
+
import { Colors } from '../../configs';
|
|
9
|
+
import Text from '../../commons/Text';
|
|
10
|
+
import RadioCircle from '../../commons/RadioCircle';
|
|
11
|
+
import BottomButtonView from '../../commons/BottomButtonView';
|
|
12
|
+
import Routes from '../../utils/Route';
|
|
13
|
+
import { axiosPatch } from '../../utils/Apis/axios';
|
|
14
|
+
import { API } from '../../configs';
|
|
15
|
+
import { TESTID } from '../../configs/Constants';
|
|
16
|
+
|
|
17
|
+
const RowSubUnit = ({ subUnit, isSelected, onSelect }) => {
|
|
18
|
+
const handleOnPress = useCallback(() => {
|
|
19
|
+
onSelect(subUnit);
|
|
20
|
+
}, [onSelect, subUnit]);
|
|
21
|
+
return (
|
|
22
|
+
<TouchableOpacity style={styles.rowSubUnit} onPress={handleOnPress}>
|
|
23
|
+
<RadioCircle active={isSelected} />
|
|
24
|
+
<View style={styles.wrapText}>
|
|
25
|
+
<Text type="H4" color={Colors.Gray9}>
|
|
26
|
+
{subUnit.name}
|
|
27
|
+
</Text>
|
|
28
|
+
</View>
|
|
29
|
+
</TouchableOpacity>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const MoveToAnotherSubUnit = memo(({ route }) => {
|
|
34
|
+
const t = useTranslations();
|
|
35
|
+
const { params = {} } = route;
|
|
36
|
+
const { unit, sensor, station } = params;
|
|
37
|
+
const { navigate } = useNavigation();
|
|
38
|
+
const [selectedSubUnit, setSelectedSubUnit] = useState(station);
|
|
39
|
+
|
|
40
|
+
const listStationUnit = useMemo(() => {
|
|
41
|
+
return unit.stations.slice(2);
|
|
42
|
+
}, [unit.stations]);
|
|
43
|
+
|
|
44
|
+
const handleOnSelect = useCallback((item) => {
|
|
45
|
+
setSelectedSubUnit(item);
|
|
46
|
+
}, []);
|
|
47
|
+
|
|
48
|
+
const onSubmit = useCallback(async () => {
|
|
49
|
+
const { success } = await axiosPatch(
|
|
50
|
+
API.SENSOR.CHANGE_SUB_UNIT(unit.id, station.id, sensor.id),
|
|
51
|
+
{
|
|
52
|
+
station_id: selectedSubUnit.id,
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
if (success) {
|
|
56
|
+
navigate(Routes.UnitDetail);
|
|
57
|
+
}
|
|
58
|
+
}, [navigate, selectedSubUnit.id, sensor.id, station.id, unit.id]);
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<View style={styles.wrap}>
|
|
62
|
+
<HeaderCustom title={t('move_to_another_sub_unit')} isShowSeparator />
|
|
63
|
+
<ScrollView>
|
|
64
|
+
<View style={styles.container}>
|
|
65
|
+
<View>
|
|
66
|
+
{listStationUnit.map((item, index) => (
|
|
67
|
+
<RowSubUnit
|
|
68
|
+
subUnit={item}
|
|
69
|
+
isSelected={selectedSubUnit === item}
|
|
70
|
+
onSelect={handleOnSelect}
|
|
71
|
+
testID={TESTID.ROW_SUB_UNIT}
|
|
72
|
+
key={index}
|
|
73
|
+
/>
|
|
74
|
+
))}
|
|
75
|
+
</View>
|
|
76
|
+
</View>
|
|
77
|
+
</ScrollView>
|
|
78
|
+
<BottomButtonView
|
|
79
|
+
style={styles.bottomButtonView}
|
|
80
|
+
mainTitle={t('text_submit')}
|
|
81
|
+
onPressMain={onSubmit}
|
|
82
|
+
typeMain={'primary'}
|
|
83
|
+
/>
|
|
84
|
+
</View>
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export default MoveToAnotherSubUnit;
|