@eohjsc/react-native-smart-city 0.3.87 → 0.3.89
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/Device/FlatListItems.js +1 -1
- package/src/commons/Device/HistoryChart.js +2 -2
- package/src/commons/Device/LinearChart.js +1 -0
- package/src/commons/Device/PMSensor/PMSensorIndicator.js +1 -5
- package/src/commons/Device/WindDirection/Compass/index.js +1 -0
- package/src/commons/MenuActionAddnew/__test__/MenuActionAddNew.test.js +1 -1
- package/src/commons/MenuActionList/__test__/MenuActionList.test.js +1 -1
- package/src/commons/ModalPopupCT/index.js +31 -25
- package/src/commons/SubUnit/OneTap/index.js +2 -1
- package/src/commons/UnitSummary/AirQuality/index.js +1 -1
- package/src/configs/API.js +1 -1
- package/src/configs/AccessibilityLabel.js +3 -0
- package/src/context/SCContext.tsx +1 -1
- package/src/screens/AddLocationMaps/index.js +5 -4
- package/src/screens/AllGateway/hooks/__test__/index.test.js +26 -2
- package/src/screens/AllGateway/hooks/useGateway.js +11 -9
- package/src/screens/Automate/MultiUnits.js +1 -0
- package/src/screens/Automate/index.js +1 -2
- package/src/screens/Device/detail.js +1 -1
- package/src/screens/Sharing/InfoMemberUnit.js +41 -8
- package/src/screens/Sharing/Styles/inforMemberUnitStyles.js +11 -0
- package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +62 -2
- package/src/screens/SubUnit/AddSubUnit.js +8 -5
- package/src/screens/Unit/ChooseLocationStyles.js +1 -0
- package/src/screens/Unit/Station/index.js +2 -1
- package/src/screens/Unit/Summaries.js +5 -1
- package/src/screens/Unit/hook/__test__/useUnitConnectRemoteDevices.test.js +57 -0
- package/src/utils/I18n/translations/en.json +4 -1
- package/src/utils/I18n/translations/vi.json +3 -0
- package/src/utils/Utils.js +2 -0
package/package.json
CHANGED
|
@@ -46,7 +46,7 @@ const FlatListItems = memo(({ data, style, title, offsetTitle }) => {
|
|
|
46
46
|
}
|
|
47
47
|
return items.map((item, index) => (
|
|
48
48
|
<QualityIndicatorItem
|
|
49
|
-
key={item?.id}
|
|
49
|
+
key={(item?.id || index).toString()}
|
|
50
50
|
standard={item.standard}
|
|
51
51
|
value={item.value}
|
|
52
52
|
measure={item.unit}
|
|
@@ -162,7 +162,7 @@ const HistoryChart = memo(
|
|
|
162
162
|
return roundedSum.toFixed();
|
|
163
163
|
}, [configuration, datas, chartConfig]);
|
|
164
164
|
|
|
165
|
-
const renderChart =
|
|
165
|
+
const renderChart = useMemo(() => {
|
|
166
166
|
if (configuration.type === 'line_chart') {
|
|
167
167
|
return (
|
|
168
168
|
<View style={styles.chartContainer}>
|
|
@@ -228,7 +228,7 @@ const HistoryChart = memo(
|
|
|
228
228
|
</View>
|
|
229
229
|
</View>
|
|
230
230
|
)}
|
|
231
|
-
{renderChart
|
|
231
|
+
{renderChart}
|
|
232
232
|
{configuration.config === 'power_consumption' && !!chartConfig.price && (
|
|
233
233
|
<Text type="H4">
|
|
234
234
|
{t('total_power_price')} <Text bold>{formatMoney(totalPrice)}</Text>
|
|
@@ -3,12 +3,8 @@ import { View } from 'react-native';
|
|
|
3
3
|
import { FlatList } from 'react-native';
|
|
4
4
|
import QualityIndicatorItem from '../WaterQualitySensor/QualityIndicatorsItem';
|
|
5
5
|
import styles from './PMSensorIndicatorStyles';
|
|
6
|
-
import { roundNumber } from '../../../utils/Utils';
|
|
6
|
+
import { keyExtractor, roundNumber } from '../../../utils/Utils';
|
|
7
7
|
|
|
8
|
-
//using for PM2.5-10, CO, UV, Rainflow Sensor
|
|
9
|
-
const keyExtractor = (item, index) => {
|
|
10
|
-
return index;
|
|
11
|
-
};
|
|
12
8
|
const PMSensorIndicator = memo(({ data = [], style }) => {
|
|
13
9
|
const renderItem = useCallback(
|
|
14
10
|
({ item }) => {
|
|
@@ -17,7 +17,7 @@ describe('Test MenuActionAddNew', () => {
|
|
|
17
17
|
let wrapper;
|
|
18
18
|
|
|
19
19
|
// flat list issue
|
|
20
|
-
it
|
|
20
|
+
it('onItemClick MenuActionAddNew', async () => {
|
|
21
21
|
const mockFunc = jest.fn();
|
|
22
22
|
await act(async () => {
|
|
23
23
|
wrapper = create(wrapComponent(dataActions));
|
|
@@ -9,7 +9,7 @@ describe('Test MenuActionList', () => {
|
|
|
9
9
|
const listItem = [item(1)];
|
|
10
10
|
let wrapper;
|
|
11
11
|
|
|
12
|
-
it
|
|
12
|
+
it('onItemClick MenuActionList', async () => {
|
|
13
13
|
const mockOnItemClick = jest.fn();
|
|
14
14
|
const mockHideModal = jest.fn();
|
|
15
15
|
|
|
@@ -20,6 +20,8 @@ const ModalPopupCT = ({
|
|
|
20
20
|
isVisible = false,
|
|
21
21
|
onPressConfirm,
|
|
22
22
|
onPressCancel,
|
|
23
|
+
textShowAlert,
|
|
24
|
+
footer,
|
|
23
25
|
}) => {
|
|
24
26
|
const [isChecked, setIsChecked] = useState(false);
|
|
25
27
|
const isUnHideButton = useMemo(() => {
|
|
@@ -59,7 +61,7 @@ const ModalPopupCT = ({
|
|
|
59
61
|
<View style={styles.viewIconAlert}>{iconAlert}</View>
|
|
60
62
|
<View style={styles.viewTextAlert}>
|
|
61
63
|
<Text type="H4" color={Colors.Gray9}>
|
|
62
|
-
{t('text_alert_modal_popup_ct')}
|
|
64
|
+
{textShowAlert || t('text_alert_modal_popup_ct')}
|
|
63
65
|
</Text>
|
|
64
66
|
</View>
|
|
65
67
|
</View>
|
|
@@ -94,41 +96,45 @@ const ModalPopupCT = ({
|
|
|
94
96
|
</View>
|
|
95
97
|
)}
|
|
96
98
|
</View>
|
|
97
|
-
|
|
98
|
-
<View style={styles.
|
|
99
|
-
<
|
|
100
|
-
onPress={onPressCancel}
|
|
101
|
-
style={styles.buttonCancel}
|
|
102
|
-
>
|
|
103
|
-
<Text
|
|
104
|
-
type="H4"
|
|
105
|
-
bold
|
|
106
|
-
color={Colors.Gray9}
|
|
107
|
-
style={styles.paddingBottom3}
|
|
108
|
-
>
|
|
109
|
-
{t('cancel')}
|
|
110
|
-
</Text>
|
|
111
|
-
</TouchableOpacity>
|
|
112
|
-
<View style={styles.buttonConfirm}>
|
|
99
|
+
{footer || (
|
|
100
|
+
<View style={styles.footer}>
|
|
101
|
+
<View style={styles.buttonBottom}>
|
|
113
102
|
<TouchableOpacity
|
|
114
|
-
onPress={
|
|
115
|
-
|
|
103
|
+
onPress={onPressCancel}
|
|
104
|
+
style={styles.buttonCancel}
|
|
116
105
|
>
|
|
117
106
|
<Text
|
|
118
107
|
type="H4"
|
|
119
108
|
bold
|
|
120
|
-
color={Colors.
|
|
109
|
+
color={Colors.Gray9}
|
|
121
110
|
style={styles.paddingBottom3}
|
|
122
111
|
>
|
|
123
|
-
{t(
|
|
112
|
+
{t('cancel')}
|
|
124
113
|
</Text>
|
|
125
114
|
</TouchableOpacity>
|
|
126
|
-
<View
|
|
127
|
-
|
|
128
|
-
|
|
115
|
+
<View style={styles.buttonConfirm}>
|
|
116
|
+
<TouchableOpacity
|
|
117
|
+
onPress={onPressConfirm}
|
|
118
|
+
disabled={isUnHideButton ? !isUnHideButton : !isChecked}
|
|
119
|
+
>
|
|
120
|
+
<Text
|
|
121
|
+
type="H4"
|
|
122
|
+
bold
|
|
123
|
+
color={Colors.White}
|
|
124
|
+
style={styles.paddingBottom3}
|
|
125
|
+
>
|
|
126
|
+
{t((type === 'reboot' && 'reboot') || 'delete')}
|
|
127
|
+
</Text>
|
|
128
|
+
</TouchableOpacity>
|
|
129
|
+
<View
|
|
130
|
+
style={
|
|
131
|
+
isUnHideButton ? {} : !isChecked && styles.disableButton
|
|
132
|
+
}
|
|
133
|
+
/>
|
|
134
|
+
</View>
|
|
129
135
|
</View>
|
|
130
136
|
</View>
|
|
131
|
-
|
|
137
|
+
)}
|
|
132
138
|
</View>
|
|
133
139
|
</ModalCustom>
|
|
134
140
|
);
|
|
@@ -49,6 +49,7 @@ const SubUnitAutomate = ({ isOwner, listAutomate, unit, wrapItemStyle }) => {
|
|
|
49
49
|
>
|
|
50
50
|
{listAutomate.map((item, index) => (
|
|
51
51
|
<TouchableOpacity
|
|
52
|
+
key={(item?.id || index).toString()}
|
|
52
53
|
style={
|
|
53
54
|
indexAutomate === index
|
|
54
55
|
? styles.borderSelection
|
|
@@ -79,7 +80,7 @@ const SubUnitAutomate = ({ isOwner, listAutomate, unit, wrapItemStyle }) => {
|
|
|
79
80
|
isOwner={isOwner}
|
|
80
81
|
automate={item}
|
|
81
82
|
unit={unit}
|
|
82
|
-
key={item.id}
|
|
83
|
+
key={item.id.toString()}
|
|
83
84
|
/>
|
|
84
85
|
))}
|
|
85
86
|
<ItemAddNew
|
|
@@ -146,7 +146,7 @@ const AirQuality = memo(({ summaryDetail }) => {
|
|
|
146
146
|
{!!advices &&
|
|
147
147
|
advices.map((item, index) => {
|
|
148
148
|
return (
|
|
149
|
-
<View key={index} style={styles.boxContentHealth}>
|
|
149
|
+
<View key={index.toString()} style={styles.boxContentHealth}>
|
|
150
150
|
<View
|
|
151
151
|
style={[styles.boxDot, { backgroundColor: outdoorColor }]}
|
|
152
152
|
/>
|
package/src/configs/API.js
CHANGED
|
@@ -4,7 +4,7 @@ const API = {
|
|
|
4
4
|
UNIT: {
|
|
5
5
|
MY_UNITS: () => '/property_manager/units/mine/',
|
|
6
6
|
SHARED_UNITS: () => '/property_manager/shared_units/',
|
|
7
|
-
CREATE_UNIT: () => '/property_manager/units/',
|
|
7
|
+
CREATE_UNIT: () => '/property_manager/iot_dashboard/dev_mode/units/',
|
|
8
8
|
UNIT_DETAIL: (id) => `/property_manager/units/${id}/`,
|
|
9
9
|
UNITS_PUBLIC: () => '/property_manager/units/public/',
|
|
10
10
|
UNIT_SUMMARY: (id) => `/property_manager/units/${id}/summary/`,
|
|
@@ -41,10 +41,11 @@ const AddLocationMaps = memo(() => {
|
|
|
41
41
|
|
|
42
42
|
const onDone = useCallback(() => {
|
|
43
43
|
navigate(Routes.AddSubUnit, {
|
|
44
|
-
location:
|
|
44
|
+
location: searchedLocation,
|
|
45
45
|
isAddUnit: true,
|
|
46
46
|
});
|
|
47
|
-
}, [
|
|
47
|
+
}, [searchedLocation, navigate]);
|
|
48
|
+
|
|
48
49
|
const onBack = useCallback(() => {
|
|
49
50
|
goBack();
|
|
50
51
|
}, [goBack]);
|
|
@@ -95,7 +96,7 @@ const AddLocationMaps = memo(() => {
|
|
|
95
96
|
}, []);
|
|
96
97
|
|
|
97
98
|
const onPressRowLocation = useCallback(async (item) => {
|
|
98
|
-
setInput(item
|
|
99
|
+
setInput(item);
|
|
99
100
|
setSearchData([]);
|
|
100
101
|
const body = {
|
|
101
102
|
params: {
|
|
@@ -111,7 +112,7 @@ const AddLocationMaps = memo(() => {
|
|
|
111
112
|
if (success) {
|
|
112
113
|
const { location } = data.result.geometry;
|
|
113
114
|
setSearchedLocation({
|
|
114
|
-
description: item
|
|
115
|
+
description: item?.description,
|
|
115
116
|
latitude: location.lat,
|
|
116
117
|
longitude: location.lng,
|
|
117
118
|
});
|
|
@@ -17,13 +17,15 @@ jest.mock('@react-navigation/native', () => {
|
|
|
17
17
|
});
|
|
18
18
|
describe('Test useGateway', () => {
|
|
19
19
|
let hook;
|
|
20
|
+
|
|
20
21
|
beforeEach(async () => {
|
|
22
|
+
mock.reset();
|
|
21
23
|
await act(() => {
|
|
22
24
|
hook = renderHook(() => useGateway());
|
|
23
25
|
});
|
|
24
26
|
});
|
|
27
|
+
|
|
25
28
|
afterEach(() => {
|
|
26
|
-
mock.reset();
|
|
27
29
|
Toast.show.mockClear();
|
|
28
30
|
});
|
|
29
31
|
|
|
@@ -46,9 +48,17 @@ describe('Test useGateway', () => {
|
|
|
46
48
|
.reply(200, { results: [{ id: 1, name: 'chip 1' }] });
|
|
47
49
|
|
|
48
50
|
await act(async () => {
|
|
49
|
-
await hook.result.current.fetchDataGateways();
|
|
51
|
+
await hook.result.current.fetchDataGateways(1, 1);
|
|
50
52
|
});
|
|
51
53
|
expect(hook.result.current.gateways).toEqual([{ id: 1, name: 'chip 1' }]);
|
|
54
|
+
|
|
55
|
+
await act(async () => {
|
|
56
|
+
await hook.result.current.fetchDataGateways(2, 1);
|
|
57
|
+
});
|
|
58
|
+
expect(hook.result.current.gateways).toEqual([
|
|
59
|
+
{ id: 1, name: 'chip 1' },
|
|
60
|
+
{ id: 1, name: 'chip 1' },
|
|
61
|
+
]);
|
|
52
62
|
});
|
|
53
63
|
|
|
54
64
|
it('test useGateway fetchDevicesGateway isInterval', async () => {
|
|
@@ -103,4 +113,18 @@ describe('Test useGateway', () => {
|
|
|
103
113
|
visibilityTime: 1000,
|
|
104
114
|
});
|
|
105
115
|
});
|
|
116
|
+
|
|
117
|
+
it('test fetchActionConfigDevice', async () => {
|
|
118
|
+
mock.onDelete(API.DEV_MODE.ZIGBEE.CONFIG_MAP(1, 1)).reply(200);
|
|
119
|
+
await act(async () => {
|
|
120
|
+
await hook.result.current.fetchActionConfigDevice(1, 1, true, false);
|
|
121
|
+
});
|
|
122
|
+
expect(hook.result.current.detailDeviceZigbee).toEqual({});
|
|
123
|
+
|
|
124
|
+
mock.onDelete(API.DEV_MODE.ZIGBEE.ACTION(1, 1)).reply(200);
|
|
125
|
+
await act(async () => {
|
|
126
|
+
await hook.result.current.fetchActionConfigDevice(1, 1, true, true);
|
|
127
|
+
});
|
|
128
|
+
expect(hook.result.current.detailDeviceZigbee).toEqual({});
|
|
129
|
+
});
|
|
106
130
|
});
|
|
@@ -68,7 +68,9 @@ export const useGateway = () => {
|
|
|
68
68
|
const config_read = data
|
|
69
69
|
?.filter(
|
|
70
70
|
(item) =>
|
|
71
|
-
item?.config
|
|
71
|
+
!item?.config.is_write &&
|
|
72
|
+
item?.config?.is_read &&
|
|
73
|
+
item?.value_type !== VIRTUAL_TYPE
|
|
72
74
|
)
|
|
73
75
|
?.map((item) => {
|
|
74
76
|
return {
|
|
@@ -86,12 +88,10 @@ export const useGateway = () => {
|
|
|
86
88
|
});
|
|
87
89
|
const config_virtual = data
|
|
88
90
|
?.filter((item) => item?.value_type === VIRTUAL_TYPE)
|
|
89
|
-
?.map((item) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
};
|
|
94
|
-
});
|
|
91
|
+
?.map((item) => ({
|
|
92
|
+
...item,
|
|
93
|
+
pin_number: item?.pin_number.toString(),
|
|
94
|
+
}));
|
|
95
95
|
|
|
96
96
|
setDetailDeviceInternal((prev) => ({
|
|
97
97
|
...prev,
|
|
@@ -164,8 +164,10 @@ export const useGateway = () => {
|
|
|
164
164
|
);
|
|
165
165
|
success && setGatewayDevices((prev) => ({ ...prev, modbus: [] }));
|
|
166
166
|
}
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
if (success) {
|
|
168
|
+
ToastBottomHelper.success(t('delete_successfully'));
|
|
169
|
+
navigation.pop(numberGoBack);
|
|
170
|
+
}
|
|
169
171
|
},
|
|
170
172
|
[navigation]
|
|
171
173
|
);
|
|
@@ -706,7 +706,7 @@ const DeviceDetail = ({ route }) => {
|
|
|
706
706
|
return (
|
|
707
707
|
<SensorDisplayItem
|
|
708
708
|
accessibilityLabel={AccessibilityLabel.SENSOR_DISPLAY_ITEM}
|
|
709
|
-
key={index}
|
|
709
|
+
key={(item?.id || index).toString()}
|
|
710
710
|
item={item}
|
|
711
711
|
evaluate={evaluate}
|
|
712
712
|
emergency={onEmergencyButtonPress}
|
|
@@ -9,7 +9,7 @@ import { HeaderCustom } from '../../commons/Header';
|
|
|
9
9
|
import RowMemberInfo from '../GuestInfo/components/RowGuestInfo';
|
|
10
10
|
import { useIsOwnerOfUnit } from '../../hooks/Common';
|
|
11
11
|
import { axiosGet } from '../../utils/Apis/axios';
|
|
12
|
-
import { AlertAction } from '../../commons';
|
|
12
|
+
import { AlertAction, ViewButtonBottom } from '../../commons';
|
|
13
13
|
import { useStateAlertAction, useDataMember } from './hooks';
|
|
14
14
|
import ItemChangeRole from './Components/ItemChangeRole';
|
|
15
15
|
import MemberSvg from '../../Images/Common/member.svg';
|
|
@@ -18,6 +18,7 @@ import styles from './Styles/inforMemberUnitStyles';
|
|
|
18
18
|
import { useNavigation, useIsFocused } from '@react-navigation/native';
|
|
19
19
|
import Routes from '../../utils/Route';
|
|
20
20
|
import { AccessibilityLabel } from '../../configs/Constants';
|
|
21
|
+
import ModalPopupCT from '../../commons/ModalPopupCT';
|
|
21
22
|
|
|
22
23
|
const InfoMemberUnit = memo(({ route }) => {
|
|
23
24
|
const t = useTranslations();
|
|
@@ -28,6 +29,7 @@ const InfoMemberUnit = memo(({ route }) => {
|
|
|
28
29
|
};
|
|
29
30
|
const { navigate } = useNavigation();
|
|
30
31
|
const [isLoading, setIsLoading] = useState(true);
|
|
32
|
+
const [isShowWarning, setIsShowWarning] = useState(false);
|
|
31
33
|
const [memberInfo, setMemberInfo] = useState({});
|
|
32
34
|
const [itemSelected, setItemSelected] = useState({});
|
|
33
35
|
const isFocused = useIsFocused();
|
|
@@ -56,10 +58,7 @@ const InfoMemberUnit = memo(({ route }) => {
|
|
|
56
58
|
if (stateAlertAction?.is_change) {
|
|
57
59
|
hideAlertAction();
|
|
58
60
|
if (itemSelected?.role?.is_owner) {
|
|
59
|
-
|
|
60
|
-
dataParams: { unit_id: unit?.id, member },
|
|
61
|
-
type: 'infoMemberUnit',
|
|
62
|
-
});
|
|
61
|
+
setIsShowWarning(true);
|
|
63
62
|
}
|
|
64
63
|
} else {
|
|
65
64
|
removeMember(
|
|
@@ -72,14 +71,19 @@ const InfoMemberUnit = memo(({ route }) => {
|
|
|
72
71
|
}, [
|
|
73
72
|
hideAlertAction,
|
|
74
73
|
itemSelected?.role?.is_owner,
|
|
75
|
-
member,
|
|
76
|
-
navigate,
|
|
77
74
|
removeMember,
|
|
78
75
|
stateAlertAction?.is_change,
|
|
79
76
|
stateAlertAction?.member,
|
|
80
|
-
unit?.id,
|
|
81
77
|
]);
|
|
82
78
|
|
|
79
|
+
const handleChangeOwner = useCallback(() => {
|
|
80
|
+
setIsShowWarning(false);
|
|
81
|
+
navigate(Routes.EnterPassword, {
|
|
82
|
+
dataParams: { unit_id: unit?.id, member },
|
|
83
|
+
type: 'infoMemberUnit',
|
|
84
|
+
});
|
|
85
|
+
}, [member, navigate, unit?.id]);
|
|
86
|
+
|
|
83
87
|
const fetchMemberInfo = useCallback(async () => {
|
|
84
88
|
setIsLoading(true);
|
|
85
89
|
const { success, data } = await axiosGet(
|
|
@@ -158,6 +162,22 @@ const InfoMemberUnit = memo(({ route }) => {
|
|
|
158
162
|
return <></>;
|
|
159
163
|
}, [isOwner, navigate, unit?.id, memberInfo]);
|
|
160
164
|
|
|
165
|
+
const footerWarning = useMemo(() => {
|
|
166
|
+
return (
|
|
167
|
+
<>
|
|
168
|
+
<View style={styles.line} />
|
|
169
|
+
<ViewButtonBottom
|
|
170
|
+
leftTitle={t('cancel')}
|
|
171
|
+
rightTitle={t('change')}
|
|
172
|
+
styleButton={styles.styleButton}
|
|
173
|
+
styleButtonLeftText={styles.textButton}
|
|
174
|
+
onLeftClick={() => setIsShowWarning(false)}
|
|
175
|
+
onRightClick={handleChangeOwner}
|
|
176
|
+
/>
|
|
177
|
+
</>
|
|
178
|
+
);
|
|
179
|
+
}, [handleChangeOwner, t]);
|
|
180
|
+
|
|
161
181
|
useEffect(() => {
|
|
162
182
|
if (isIdentityOwner) {
|
|
163
183
|
setItemSelected(itemsRoleModal[0]);
|
|
@@ -223,6 +243,7 @@ const InfoMemberUnit = memo(({ route }) => {
|
|
|
223
243
|
<TouchableOpacity
|
|
224
244
|
onPress={onPressChangeRole}
|
|
225
245
|
style={styles.role}
|
|
246
|
+
accessibilityLabel={AccessibilityLabel.CHANGE_ROLE}
|
|
226
247
|
>
|
|
227
248
|
<View style={styles.leftRole}>
|
|
228
249
|
<Text type="Body" color={Colors.Gray7}>
|
|
@@ -271,6 +292,18 @@ const InfoMemberUnit = memo(({ route }) => {
|
|
|
271
292
|
>
|
|
272
293
|
<ChangeRoleContent />
|
|
273
294
|
</AlertAction>
|
|
295
|
+
|
|
296
|
+
<ModalPopupCT
|
|
297
|
+
isVisible={isShowWarning}
|
|
298
|
+
title={t('transfer_ownership')}
|
|
299
|
+
subTitle={t('text_change_owner')}
|
|
300
|
+
isShowAlert
|
|
301
|
+
isShowUnderstand={false}
|
|
302
|
+
textShowAlert={t('text_alert_change_owner')}
|
|
303
|
+
styleWrapButton={styles.wrapButton}
|
|
304
|
+
styleRightButton={styles.button}
|
|
305
|
+
footer={footerWarning}
|
|
306
|
+
/>
|
|
274
307
|
</>
|
|
275
308
|
);
|
|
276
309
|
});
|
|
@@ -89,4 +89,15 @@ export default StyleSheet.create({
|
|
|
89
89
|
removeButtonStyle: {
|
|
90
90
|
color: Colors.Red,
|
|
91
91
|
},
|
|
92
|
+
line: {
|
|
93
|
+
borderTopWidth: 1,
|
|
94
|
+
borderTopColor: Colors.Neutral.Neutral3,
|
|
95
|
+
},
|
|
96
|
+
styleButton: {
|
|
97
|
+
paddingTop: 24,
|
|
98
|
+
paddingBottom: 0,
|
|
99
|
+
},
|
|
100
|
+
textButton: {
|
|
101
|
+
color: Colors.Gray9,
|
|
102
|
+
},
|
|
92
103
|
});
|
|
@@ -7,10 +7,13 @@ import InfoMemberUnit from '../InfoMemberUnit';
|
|
|
7
7
|
import { HeaderCustom } from '../../../commons/Header';
|
|
8
8
|
import { SCProvider } from '../../../context';
|
|
9
9
|
import { mockSCStore } from '../../../context/mockStore';
|
|
10
|
-
import { AlertAction } from '../../../commons';
|
|
10
|
+
import { AlertAction, ViewButtonBottom } from '../../../commons';
|
|
11
11
|
import API from '../../../configs/API';
|
|
12
12
|
import { AccessibilityLabel } from '../../../configs/Constants';
|
|
13
13
|
import api from '../../../utils/Apis/axios';
|
|
14
|
+
import ItemChangeRole from '../Components/ItemChangeRole';
|
|
15
|
+
import ModalPopupCT from '../../../commons/ModalPopupCT';
|
|
16
|
+
import Routes from '../../../utils/Route';
|
|
14
17
|
|
|
15
18
|
const mock = new MockAdapter(api.axiosInstance);
|
|
16
19
|
|
|
@@ -53,6 +56,7 @@ describe('Test InfoMemberUnit', () => {
|
|
|
53
56
|
unit: {
|
|
54
57
|
id: 1,
|
|
55
58
|
name: 'unit',
|
|
59
|
+
user_id: 1,
|
|
56
60
|
},
|
|
57
61
|
member: {
|
|
58
62
|
id: 1,
|
|
@@ -107,7 +111,7 @@ describe('Test InfoMemberUnit', () => {
|
|
|
107
111
|
|
|
108
112
|
it('render InfoMemberUnit delete member', async () => {
|
|
109
113
|
mock.onGet(API.SHARE.UNIT_MEMBER_INFO(1, 1)).reply(200, {
|
|
110
|
-
id:
|
|
114
|
+
id: 2,
|
|
111
115
|
identity: 'owner',
|
|
112
116
|
phone_number: '88888',
|
|
113
117
|
name: 'user',
|
|
@@ -131,4 +135,60 @@ describe('Test InfoMemberUnit', () => {
|
|
|
131
135
|
});
|
|
132
136
|
expect(mockGoBack).toBeCalled();
|
|
133
137
|
});
|
|
138
|
+
|
|
139
|
+
it('test change owner', async () => {
|
|
140
|
+
mock.onGet(API.SHARE.UNIT_MEMBER_INFO(1, 1)).reply(200, {
|
|
141
|
+
id: 2,
|
|
142
|
+
identity: 'owner',
|
|
143
|
+
phone_number: '88888',
|
|
144
|
+
name: 'user',
|
|
145
|
+
email: 'abc@gmail.com',
|
|
146
|
+
});
|
|
147
|
+
await act(async () => {
|
|
148
|
+
tree = await create(wrapComponent(route));
|
|
149
|
+
});
|
|
150
|
+
const instance = tree.root;
|
|
151
|
+
|
|
152
|
+
const touchChangeRole = instance.findByProps({
|
|
153
|
+
accessibilityLabel: AccessibilityLabel.CHANGE_ROLE,
|
|
154
|
+
});
|
|
155
|
+
await act(async () => {
|
|
156
|
+
await touchChangeRole.props.onPress();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const alertAction = instance.findByType(AlertAction);
|
|
160
|
+
expect(alertAction.props.visible).toEqual(true);
|
|
161
|
+
|
|
162
|
+
const itemChangeRole = instance.findAllByType(ItemChangeRole);
|
|
163
|
+
expect(itemChangeRole).toHaveLength(2);
|
|
164
|
+
|
|
165
|
+
const modalPopupCT = instance.findByType(ModalPopupCT);
|
|
166
|
+
await act(async () => {
|
|
167
|
+
await itemChangeRole[0].props.onPress();
|
|
168
|
+
});
|
|
169
|
+
await act(async () => {
|
|
170
|
+
await alertAction.props.rightButtonClick();
|
|
171
|
+
});
|
|
172
|
+
expect(alertAction.props.visible).toEqual(false);
|
|
173
|
+
expect(modalPopupCT.props.isVisible).toEqual(true);
|
|
174
|
+
|
|
175
|
+
const viewButtonBottom = instance.findAllByType(ViewButtonBottom);
|
|
176
|
+
expect(viewButtonBottom).toHaveLength(2);
|
|
177
|
+
|
|
178
|
+
await act(async () => {
|
|
179
|
+
await viewButtonBottom[1].props.onRightClick();
|
|
180
|
+
});
|
|
181
|
+
expect(modalPopupCT.props.isVisible).toEqual(false);
|
|
182
|
+
expect(mockedNavigate).toHaveBeenCalledWith(Routes.EnterPassword, {
|
|
183
|
+
dataParams: {
|
|
184
|
+
unit_id: 1,
|
|
185
|
+
member: {
|
|
186
|
+
id: 1,
|
|
187
|
+
name: 'user1',
|
|
188
|
+
share_id: 1,
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
type: 'infoMemberUnit',
|
|
192
|
+
});
|
|
193
|
+
});
|
|
134
194
|
});
|
|
@@ -29,7 +29,7 @@ const AddSubUnit = ({ route }) => {
|
|
|
29
29
|
unit,
|
|
30
30
|
addType,
|
|
31
31
|
isAddUnit,
|
|
32
|
-
location =
|
|
32
|
+
location = {},
|
|
33
33
|
isInsideUnit,
|
|
34
34
|
} = route?.params;
|
|
35
35
|
const [roomName, setRoomName] = useState('');
|
|
@@ -50,8 +50,10 @@ const AddSubUnit = ({ route }) => {
|
|
|
50
50
|
awaitCreate.current = true;
|
|
51
51
|
const dataObj = {
|
|
52
52
|
name: roomName,
|
|
53
|
-
address: location,
|
|
53
|
+
address: location.description,
|
|
54
54
|
background: wallpaper,
|
|
55
|
+
lat: location.latitude,
|
|
56
|
+
lng: location.longitude,
|
|
55
57
|
};
|
|
56
58
|
const formData = createFormData(dataObj, ['background']);
|
|
57
59
|
const { success, data } = await axiosPost(
|
|
@@ -161,7 +163,7 @@ const AddSubUnit = ({ route }) => {
|
|
|
161
163
|
|
|
162
164
|
const validateData = useMemo(() => {
|
|
163
165
|
if (isAddUnit) {
|
|
164
|
-
return roomName === '' || wallpaper === '' || location === '';
|
|
166
|
+
return roomName === '' || wallpaper === '' || location.description === '';
|
|
165
167
|
} else {
|
|
166
168
|
return roomName === '' || wallpaper === '';
|
|
167
169
|
}
|
|
@@ -212,9 +214,10 @@ const AddSubUnit = ({ route }) => {
|
|
|
212
214
|
<Text style={styles.addWallpaper}>{t('geolocation')}</Text>
|
|
213
215
|
<Text
|
|
214
216
|
style={styles.textLocation}
|
|
215
|
-
color={location && Colors.Primary}
|
|
217
|
+
color={location.description && Colors.Primary}
|
|
216
218
|
>
|
|
217
|
-
{location ||
|
|
219
|
+
{location.description ||
|
|
220
|
+
t('text_explain_add_geolocation')}
|
|
218
221
|
</Text>
|
|
219
222
|
</View>
|
|
220
223
|
</TouchableWithoutFeedback>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { memo, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { View, Text, TouchableOpacity, FlatList } from 'react-native';
|
|
3
3
|
import { AccessibilityLabel } from '../../../configs/Constants';
|
|
4
|
+
import { keyExtractor } from '../../../utils/Utils';
|
|
4
5
|
import styles from './StationStyles';
|
|
5
6
|
|
|
6
7
|
const Station = ({ listStation = [], onSnapToItem, indexStation }) => {
|
|
@@ -63,10 +64,10 @@ const Station = ({ listStation = [], onSnapToItem, indexStation }) => {
|
|
|
63
64
|
return (
|
|
64
65
|
<View style={styles.container}>
|
|
65
66
|
<FlatList
|
|
67
|
+
keyExtractor={keyExtractor}
|
|
66
68
|
removeClippedSubviews={false}
|
|
67
69
|
onScroll={onScroll}
|
|
68
70
|
ref={flatListRef}
|
|
69
|
-
keyExtractor={(item) => item.id}
|
|
70
71
|
horizontal
|
|
71
72
|
data={data}
|
|
72
73
|
extraData={data}
|
|
@@ -107,7 +107,11 @@ const Summaries = memo(({ unit }) => {
|
|
|
107
107
|
accessibilityLabel={AccessibilityLabel.UNIT_DETAIL_UNIT_SUMMARY_VIEW}
|
|
108
108
|
>
|
|
109
109
|
{unitSummaries.map((item, index) => (
|
|
110
|
-
<SummaryItem
|
|
110
|
+
<SummaryItem
|
|
111
|
+
key={(item?.id || index).toString()}
|
|
112
|
+
item={item}
|
|
113
|
+
goToSummary={goToSummary}
|
|
114
|
+
/>
|
|
111
115
|
))}
|
|
112
116
|
</ScrollView>
|
|
113
117
|
)}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { renderHook } from '@testing-library/react-hooks';
|
|
3
|
+
import { SCProvider } from '../../../../context';
|
|
4
|
+
import { mockSCStore } from '../../../../context/mockStore';
|
|
5
|
+
import { useUnitConnectRemoteDevices } from '../useUnitConnectRemoteDevices';
|
|
6
|
+
|
|
7
|
+
const mockSetAction = jest.fn();
|
|
8
|
+
|
|
9
|
+
const wrapper = ({ children }) => <SCProvider>{children}</SCProvider>;
|
|
10
|
+
|
|
11
|
+
jest.mock('react', () => {
|
|
12
|
+
return {
|
|
13
|
+
...jest.requireActual('react'),
|
|
14
|
+
useContext: () => ({
|
|
15
|
+
setAction: mockSetAction,
|
|
16
|
+
stateData: mockSCStore({
|
|
17
|
+
app: {
|
|
18
|
+
isNetworkConnected: true,
|
|
19
|
+
},
|
|
20
|
+
bluetooth: {
|
|
21
|
+
isEnabled: true,
|
|
22
|
+
},
|
|
23
|
+
iot: {
|
|
24
|
+
lgthinq: {
|
|
25
|
+
unitConnected: [
|
|
26
|
+
{
|
|
27
|
+
id: 1,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('Test useUnitConnectRemoteDevices', () => {
|
|
38
|
+
const unit = {
|
|
39
|
+
id: 1,
|
|
40
|
+
remote_control_options: {
|
|
41
|
+
lg_thinq: [
|
|
42
|
+
{
|
|
43
|
+
id: 1,
|
|
44
|
+
lg_devices: [{ device_id: 1, configs: [{ id: 1, name: 'name' }] }],
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
bluetooth: [1],
|
|
48
|
+
googlehome: [1],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
it('test handleLgThinqConnect', async () => {
|
|
52
|
+
renderHook(() => useUnitConnectRemoteDevices(unit), {
|
|
53
|
+
wrapper,
|
|
54
|
+
});
|
|
55
|
+
expect(mockSetAction).not.toBeCalled();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -675,7 +675,7 @@
|
|
|
675
675
|
"text_notification_content_turbility_high": "The Turbility at **unit_name** is **status**. Water is not safe to drink.",
|
|
676
676
|
"text_notification_content_smoke": "Smoke appears in **unit_name**. Please check your home and call the rescue team if there is a fire.",
|
|
677
677
|
"text_notification_content_fire": "There is a fire at **unit_name**, Please move out of the house immediately and call the rescue team.",
|
|
678
|
-
"text_notification_content_active_sos": "**device_name** is activating at **unit_name** - **station_name
|
|
678
|
+
"text_notification_content_active_sos": "**device_name** is activating at **unit_name** - **station_name**. Please check it NOW.",
|
|
679
679
|
"text_notification_content_replace_water_filter": "Filter **config_name** of **device_name** in **station_name** has less than 10h remaining. Check and replace now.",
|
|
680
680
|
"text_notification_content_low_battery": "Battery of **device_name** in **unit_name**: **station_name** has less than 20%. Check and replace now.",
|
|
681
681
|
"text_notification_content_remove_unit_to_owner": "Unit **unit_name** has been removed successfully.",
|
|
@@ -1125,5 +1125,8 @@
|
|
|
1125
1125
|
"data_address": "Data address",
|
|
1126
1126
|
"methods": "{number} methods",
|
|
1127
1127
|
"io_method": "Input/Output method",
|
|
1128
|
+
"transfer_ownership": "Transfer ownership",
|
|
1129
|
+
"text_change_owner": "Ownership permissions of this unit to the user you have selected.",
|
|
1130
|
+
"text_alert_change_owner": "You will be removed from this Unit after transfer ownership.",
|
|
1128
1131
|
"the_system_has_been_upgraded" : "To provide a better user experience, [Era] will perform a system upgrade.The upgrade will take about 10 minutes.\n\n Some device control functions will be interrupted during the upgrade.[Era] would like to ask for your understanding for this inconvenience.\n\nBest regards."
|
|
1129
1132
|
}
|
|
@@ -1124,6 +1124,9 @@
|
|
|
1124
1124
|
"data_address": "Địa chỉ dữ liệu",
|
|
1125
1125
|
"methods": "{number} phương thức",
|
|
1126
1126
|
"io_method": "Giao thức Input/Output",
|
|
1127
|
+
"transfer_ownership": "Chuyển quyền sở hữu",
|
|
1128
|
+
"text_change_owner": "Quyền sở hữu đơn vị này cho người dùng bạn đã chọn.",
|
|
1129
|
+
"text_alert_change_owner": "Bạn sẽ bị xóa khỏi Unit này sau khi chuyển quyền sở hữu.",
|
|
1127
1130
|
"the_system_has_been_upgraded" : "Để mang lại trải nghiệm tốt hơn cho người dùng, [Era] sẽ tiến hành nâng cấp hệ thống.Việc nâng cấp sẽ diễn ra trong khoảng 10 phút.\n\nMột số chức năng điều khiển thiết bị sẽ bị gián đoạn trong thời gian nâng cấp.[Era] kính mong quý khách hàng thông cảm cho sự bất tiện này.\n\nTrân trọng "
|
|
1128
1131
|
|
|
1129
1132
|
}
|