@eohjsc/react-native-smart-city 0.7.26 → 0.7.30
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/index.js +2 -0
- package/package.json +2 -1
- package/src/commons/Dashboard/MyDashboardDevice/__test__/index.test.js +68 -0
- package/src/commons/Dashboard/MyDashboardDevice/index.js +46 -11
- package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +43 -11
- package/src/commons/Dashboard/MyUnit/index.js +40 -32
- package/src/commons/ModalAlert/index.js +51 -0
- package/src/commons/ModalAlert/styles.js +54 -0
- package/src/commons/SubUnit/ShortDetail.js +20 -4
- package/src/commons/SubUnit/__test__/ShortDetail.test.js +46 -1
- package/src/configs/API.js +6 -0
- package/src/configs/AccessibilityLabel.js +1 -0
- package/src/configs/Constants.js +7 -0
- package/src/configs/SCConfig.js +6 -0
- package/src/context/SCContext.tsx +12 -1
- package/src/context/SCStore.ts +14 -0
- package/src/context/actionType.ts +10 -0
- package/src/context/mockStore.ts +30 -1
- package/src/context/reducer.ts +35 -0
- package/src/hooks/IoT/useRemoteControl.js +4 -1
- package/src/hooks/IoT/useWatchSharedChips.js +130 -0
- package/src/hooks/Review/__test__/useInAppReview.test.js +99 -0
- package/src/hooks/Review/useInAppReview.js +70 -0
- package/src/hooks/useMqtt.js +78 -27
- package/src/iot/Monitor.js +149 -26
- package/src/iot/UpdateStates.js +60 -0
- package/src/iot/mqtt.js +177 -22
- package/src/navigations/UnitStack.js +16 -0
- package/src/screens/ActivityLog/FilterPopup.js +4 -79
- package/src/screens/ActivityLog/ItemLog.js +1 -0
- package/src/screens/ActivityLog/__test__/FilterPopup.test.js +2 -6
- package/src/screens/ActivityLog/__test__/index.test.js +51 -29
- package/src/screens/ActivityLog/index.js +0 -1
- package/src/screens/ActivityLog/styles/filterPopupStyles.js +5 -2
- package/src/screens/AddNewGateway/RenameNewDevices.js +5 -0
- package/src/screens/AddNewGateway/__test__/RenameNewDevices.test.js +18 -0
- package/src/screens/Automate/AddNewAction/ReceiverSelect.js +208 -0
- package/src/screens/Automate/AddNewAction/SetupScriptEmail.js +1 -1
- package/src/screens/Automate/AddNewAction/SetupScriptNotify.js +18 -28
- package/src/screens/Automate/AddNewAction/SetupScriptReceiverEmail.js +22 -129
- package/src/screens/Automate/AddNewAction/SetupScriptReceiverNotify.js +59 -0
- package/src/screens/Automate/AddNewAction/SetupScriptReceiverSms.js +22 -129
- package/src/screens/Automate/AddNewAction/SetupScriptSms.js +1 -1
- package/src/screens/Automate/AddNewAction/Styles/{SetupScriptReceiverEmailStyles.js → ReceiverSelectStyles.js} +18 -1
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptNotify.test.js +16 -33
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverEmail.test.js +10 -8
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverNotify.test.js +217 -0
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverSms.test.js +10 -8
- package/src/screens/Automate/Components/InputName.js +5 -1
- package/src/screens/Automate/OneTap/__test__/AddNewOneTap.test.js +18 -0
- package/src/screens/Automate/ScriptDetail/index.js +6 -6
- package/src/screens/CreatePassword/__test__/index.test.js +133 -0
- package/src/screens/CreatePassword/index.js +134 -0
- package/src/screens/CreatePassword/styles.js +45 -0
- package/src/screens/Device/__test__/DeviceDetail-3rdparty.test.js +447 -0
- package/src/screens/Device/__test__/DeviceDetail-arduino.test.js +344 -0
- package/src/screens/Device/__test__/{mqttDetail.test.js → DeviceDetail-modbus.test.js} +287 -320
- package/src/screens/Device/__test__/DeviceDetail-zigbee.test.js +451 -0
- package/src/screens/Device/__test__/DeviceDetail.test.js +502 -0
- package/src/screens/Device/__test__/detail.test.js +61 -3
- package/src/screens/Device/__test__/sensorDisplayItem.test.js +28 -3
- package/src/screens/Device/components/SensorDisplayItem.js +16 -14
- package/src/screens/Device/detail.js +14 -6
- package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +3 -2
- package/src/screens/Device/styles.js +0 -5
- package/src/screens/EnterPassword/__test__/EnterPassword.test.js +76 -1
- package/src/screens/EnterPassword/index.js +34 -4
- package/src/screens/EnterPassword/styles.js +1 -1
- package/src/utils/FactoryGateway.js +597 -0
- package/src/utils/I18n/translations/en.js +10 -0
- package/src/utils/I18n/translations/vi.js +10 -0
- package/src/utils/Route/index.js +3 -1
- package/src/utils/Validation.js +5 -0
- package/src/utils/store.js +5 -0
|
@@ -3,16 +3,19 @@ import { StyleSheet } from 'react-native';
|
|
|
3
3
|
|
|
4
4
|
export default StyleSheet.create({
|
|
5
5
|
wrapPopup: {
|
|
6
|
-
marginHorizontal:
|
|
6
|
+
marginHorizontal: 12,
|
|
7
7
|
},
|
|
8
8
|
popup: {
|
|
9
9
|
backgroundColor: Colors.White,
|
|
10
10
|
width: '100%',
|
|
11
11
|
height: (Constants.height * 80) / 100,
|
|
12
|
-
padding:
|
|
12
|
+
padding: 20,
|
|
13
13
|
paddingBottom: 100,
|
|
14
14
|
borderRadius: 10,
|
|
15
15
|
},
|
|
16
|
+
datePicker: {
|
|
17
|
+
justifyContent: 'center',
|
|
18
|
+
},
|
|
16
19
|
heigh50percent: {
|
|
17
20
|
height: (Constants.height * 50) / 100,
|
|
18
21
|
},
|
|
@@ -8,6 +8,7 @@ import Text from '../../commons/Text';
|
|
|
8
8
|
import GatewayIcon from '../../../assets/images/AddNewDevice/gateway-icon.svg';
|
|
9
9
|
import _TextInput from '../../commons/Form/TextInput';
|
|
10
10
|
import AccessibilityLabel from '../../configs/AccessibilityLabel';
|
|
11
|
+
import useInAppReview from '../../hooks/Review/useInAppReview';
|
|
11
12
|
import Routes from '../../utils/Route';
|
|
12
13
|
import styles from './RenameNewDevicesStyles';
|
|
13
14
|
import { axiosGet, axiosPost } from '../../utils/Apis/axios';
|
|
@@ -33,6 +34,7 @@ const RenameNewDevices = memo(({ route }) => {
|
|
|
33
34
|
const [selectedDevice, setSelectedDevice] = useState([]);
|
|
34
35
|
const [originalDevices, setOriginalDevices] = useState([]);
|
|
35
36
|
const [isAutoFocus, setIsAutoFocus] = useState(true);
|
|
37
|
+
const { allowInAppReview } = useInAppReview(false);
|
|
36
38
|
|
|
37
39
|
const renameLabel = AccessibilityLabel.CONNECTED_DEVICE_RENAME_DEVICE;
|
|
38
40
|
|
|
@@ -94,6 +96,7 @@ const RenameNewDevices = memo(({ route }) => {
|
|
|
94
96
|
setIsDisabled(false);
|
|
95
97
|
clearTimeout(clear);
|
|
96
98
|
}, 3000);
|
|
99
|
+
allowInAppReview();
|
|
97
100
|
goBackUnitDetail();
|
|
98
101
|
return;
|
|
99
102
|
}
|
|
@@ -135,6 +138,7 @@ const RenameNewDevices = memo(({ route }) => {
|
|
|
135
138
|
}
|
|
136
139
|
|
|
137
140
|
if (success1 || success2) {
|
|
141
|
+
allowInAppReview();
|
|
138
142
|
goBackUnitDetail();
|
|
139
143
|
}
|
|
140
144
|
if (!success1) {
|
|
@@ -145,6 +149,7 @@ const RenameNewDevices = memo(({ route }) => {
|
|
|
145
149
|
}
|
|
146
150
|
setLoading(false);
|
|
147
151
|
}, [
|
|
152
|
+
allowInAppReview,
|
|
148
153
|
goBackUnitDetail,
|
|
149
154
|
info,
|
|
150
155
|
isChangedName,
|
|
@@ -14,6 +14,18 @@ import api from '../../../utils/Apis/axios';
|
|
|
14
14
|
import { CheckBoxCustom } from '../../Sharing/Components';
|
|
15
15
|
import RenameNewDevices from '../RenameNewDevices';
|
|
16
16
|
|
|
17
|
+
const mockAllowInAppReview = jest.fn();
|
|
18
|
+
const mockAskReview = jest.fn();
|
|
19
|
+
jest.mock('../../../hooks/Review/useInAppReview', () => {
|
|
20
|
+
return {
|
|
21
|
+
__esModule: true,
|
|
22
|
+
default: jest.fn(() => ({
|
|
23
|
+
allowInAppReview: mockAllowInAppReview,
|
|
24
|
+
askReview: mockAskReview,
|
|
25
|
+
})),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
17
29
|
const wrapComponent = (route) => (
|
|
18
30
|
<SCProvider initState={mockSCStore({})}>
|
|
19
31
|
<RenameNewDevices route={route} />
|
|
@@ -103,6 +115,8 @@ describe('Test rename new devices', () => {
|
|
|
103
115
|
};
|
|
104
116
|
|
|
105
117
|
beforeEach(() => {
|
|
118
|
+
mockAllowInAppReview.mockClear();
|
|
119
|
+
mockAskReview.mockClear();
|
|
106
120
|
navigate.mockClear();
|
|
107
121
|
mock.reset();
|
|
108
122
|
route = {
|
|
@@ -403,6 +417,8 @@ describe('Test rename new devices', () => {
|
|
|
403
417
|
],
|
|
404
418
|
})
|
|
405
419
|
);
|
|
420
|
+
expect(mockAllowInAppReview).toHaveBeenCalled();
|
|
421
|
+
expect(mockAskReview).not.toHaveBeenCalled();
|
|
406
422
|
mockedDispatchNavigate();
|
|
407
423
|
});
|
|
408
424
|
|
|
@@ -443,6 +459,8 @@ describe('Test rename new devices', () => {
|
|
|
443
459
|
});
|
|
444
460
|
|
|
445
461
|
expect(mock.history.post).toHaveLength(0);
|
|
462
|
+
expect(mockAllowInAppReview).toHaveBeenCalled();
|
|
463
|
+
expect(mockAskReview).not.toHaveBeenCalled();
|
|
446
464
|
mockedDispatchNavigate();
|
|
447
465
|
});
|
|
448
466
|
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState, useEffect, memo } from 'react';
|
|
2
|
+
import { FlatList, View } from 'react-native';
|
|
3
|
+
import { useNavigation } from '@react-navigation/native';
|
|
4
|
+
import CheckBox from '@react-native-community/checkbox';
|
|
5
|
+
import styles from './Styles/ReceiverSelectStyles';
|
|
6
|
+
import { CircleView, HeaderCustom, Text } from '../../../commons';
|
|
7
|
+
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
8
|
+
|
|
9
|
+
import BottomButtonView from '../../../commons/BottomButtonView';
|
|
10
|
+
import { Search } from '../../../commons/DevMode';
|
|
11
|
+
import { convertToSlug } from '../../../utils/Functions/Search';
|
|
12
|
+
import { axiosGet } from '../../../utils/Apis/axios';
|
|
13
|
+
import { API, Colors } from '../../../configs';
|
|
14
|
+
import { useSCContextSelector } from '../../../context';
|
|
15
|
+
import { Image } from 'react-native';
|
|
16
|
+
|
|
17
|
+
const ReceiverSelect = ({
|
|
18
|
+
listUser,
|
|
19
|
+
unitId,
|
|
20
|
+
title,
|
|
21
|
+
setListUser,
|
|
22
|
+
onLoadOption,
|
|
23
|
+
onNext,
|
|
24
|
+
}) => {
|
|
25
|
+
const t = useTranslations();
|
|
26
|
+
const { goBack } = useNavigation();
|
|
27
|
+
const [members, setMembers] = useState([]);
|
|
28
|
+
const [search, setSearch] = useState('');
|
|
29
|
+
const currentUserId = useSCContextSelector(
|
|
30
|
+
(state) => state.auth.account.user.id
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const loadMembers = useCallback(async () => {
|
|
34
|
+
const { success, data } = await axiosGet(API.SHARE.UNITS_MEMBERS(unitId));
|
|
35
|
+
if (success) {
|
|
36
|
+
setMembers(
|
|
37
|
+
data.map((item) => ({
|
|
38
|
+
...item,
|
|
39
|
+
...onLoadOption(item),
|
|
40
|
+
}))
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}, [unitId, onLoadOption]);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
loadMembers();
|
|
47
|
+
}, [loadMembers]);
|
|
48
|
+
|
|
49
|
+
const filterMembers = useMemo(() => {
|
|
50
|
+
return members.filter((member) => {
|
|
51
|
+
const text = convertToSlug(search);
|
|
52
|
+
const isNameMatch = convertToSlug(member.name ?? '').includes(text);
|
|
53
|
+
const isPhoneMatch = convertToSlug(member.phone_number ?? '').includes(
|
|
54
|
+
text
|
|
55
|
+
);
|
|
56
|
+
const isEmailMatch = convertToSlug(member.email ?? '').includes(text);
|
|
57
|
+
return isNameMatch || isPhoneMatch || isEmailMatch;
|
|
58
|
+
});
|
|
59
|
+
}, [members, search]);
|
|
60
|
+
|
|
61
|
+
const arrColor = useMemo(
|
|
62
|
+
() => [
|
|
63
|
+
Colors.GeekBlue3,
|
|
64
|
+
Colors.Purple3,
|
|
65
|
+
Colors.Orange3,
|
|
66
|
+
Colors.Volcano3,
|
|
67
|
+
Colors.Blue9,
|
|
68
|
+
Colors.Green3,
|
|
69
|
+
Colors.Cyan2,
|
|
70
|
+
],
|
|
71
|
+
[]
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const selectableIds = useMemo(
|
|
75
|
+
() =>
|
|
76
|
+
members.filter((member) => !member.disabled).map((member) => member.id),
|
|
77
|
+
[members]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const allSelected = useMemo(
|
|
81
|
+
() => selectableIds.length === listUser.length,
|
|
82
|
+
[listUser, selectableIds]
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const onChecked = useCallback(
|
|
86
|
+
(id) => (checked) => {
|
|
87
|
+
setListUser((prev) =>
|
|
88
|
+
checked ? [...prev, id] : prev.filter((userId) => userId !== id)
|
|
89
|
+
);
|
|
90
|
+
},
|
|
91
|
+
[setListUser]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const onDeselectAll = useCallback(() => {
|
|
95
|
+
setListUser([]);
|
|
96
|
+
}, [setListUser]);
|
|
97
|
+
|
|
98
|
+
const onSelectAll = useCallback(() => {
|
|
99
|
+
setListUser(selectableIds);
|
|
100
|
+
}, [selectableIds, setListUser]);
|
|
101
|
+
|
|
102
|
+
const onToggleAll = useCallback(
|
|
103
|
+
(checked) => {
|
|
104
|
+
if (checked) {
|
|
105
|
+
onSelectAll();
|
|
106
|
+
} else {
|
|
107
|
+
onDeselectAll();
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
[onSelectAll, onDeselectAll]
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const RowMember = memo(({ member, index, onValueChange }) => {
|
|
114
|
+
const { id, name, avatar, share_id, label, invalidLabel, disabled } =
|
|
115
|
+
member;
|
|
116
|
+
const [role, roleColor] = useMemo(() => {
|
|
117
|
+
if (!share_id) {
|
|
118
|
+
return [t('owner'), Colors.Primary];
|
|
119
|
+
}
|
|
120
|
+
if (id === currentUserId) {
|
|
121
|
+
return [t('me'), Colors.Primary];
|
|
122
|
+
}
|
|
123
|
+
return [t('member'), Colors.Gray6];
|
|
124
|
+
}, [share_id, id]);
|
|
125
|
+
|
|
126
|
+
const firstWordsInName = useMemo(() => {
|
|
127
|
+
return name.charAt();
|
|
128
|
+
}, [name]);
|
|
129
|
+
|
|
130
|
+
const circleColor = arrColor[index % arrColor.length];
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<View style={styles.rowContainer}>
|
|
134
|
+
<View style={styles.border}>
|
|
135
|
+
<CheckBox
|
|
136
|
+
disabled={disabled}
|
|
137
|
+
lineWidth={4}
|
|
138
|
+
value={listUser.includes(id)}
|
|
139
|
+
onValueChange={onValueChange(id)}
|
|
140
|
+
style={styles.checkbox}
|
|
141
|
+
/>
|
|
142
|
+
<View style={styles.paddingLeft16}>
|
|
143
|
+
{avatar ? (
|
|
144
|
+
<Image source={{ uri: avatar }} style={styles.avatar} />
|
|
145
|
+
) : (
|
|
146
|
+
<CircleView size={40} backgroundColor={circleColor} center>
|
|
147
|
+
<Text color={Colors.White}>{firstWordsInName}</Text>
|
|
148
|
+
</CircleView>
|
|
149
|
+
)}
|
|
150
|
+
</View>
|
|
151
|
+
<View style={styles.paddingLeft16}>
|
|
152
|
+
<Text style={styles.titleName}>{name}</Text>
|
|
153
|
+
{label ? (
|
|
154
|
+
<Text style={styles.status}>{label}</Text>
|
|
155
|
+
) : (
|
|
156
|
+
<Text style={styles.invalid}>{invalidLabel}</Text>
|
|
157
|
+
)}
|
|
158
|
+
</View>
|
|
159
|
+
<View style={styles.endFlex}>
|
|
160
|
+
<Text style={[styles.textRole, { color: roleColor }]}>{role}</Text>
|
|
161
|
+
</View>
|
|
162
|
+
</View>
|
|
163
|
+
</View>
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<View style={styles.wrap}>
|
|
169
|
+
<HeaderCustom isShowClose onClose={goBack} title={title} />
|
|
170
|
+
|
|
171
|
+
<View style={styles.wrapSelectAll}>
|
|
172
|
+
<Search onSearch={setSearch} />
|
|
173
|
+
<View style={styles.wrapCheckboxSelectAll}>
|
|
174
|
+
<CheckBox
|
|
175
|
+
value={allSelected}
|
|
176
|
+
onValueChange={onToggleAll}
|
|
177
|
+
style={styles.checkboxSelectAll}
|
|
178
|
+
/>
|
|
179
|
+
<Text>{allSelected ? t('deselect_all') : t('select_all')}</Text>
|
|
180
|
+
</View>
|
|
181
|
+
</View>
|
|
182
|
+
<View style={styles.borderSelectAll} />
|
|
183
|
+
|
|
184
|
+
<FlatList
|
|
185
|
+
data={filterMembers}
|
|
186
|
+
renderItem={({ item, index }) => (
|
|
187
|
+
<RowMember member={item} index={index} onValueChange={onChecked} />
|
|
188
|
+
)}
|
|
189
|
+
keyExtractor={(item) => item.id.toString()}
|
|
190
|
+
ListEmptyComponent={
|
|
191
|
+
<View style={styles.viewEmpty}>
|
|
192
|
+
<Text style={styles.textCenter}>{t('no_member')}</Text>
|
|
193
|
+
</View>
|
|
194
|
+
}
|
|
195
|
+
/>
|
|
196
|
+
<View style={styles.container}>
|
|
197
|
+
<BottomButtonView
|
|
198
|
+
style={styles.bottomButtonView}
|
|
199
|
+
mainTitle={t('done')}
|
|
200
|
+
onPressMain={onNext}
|
|
201
|
+
typeMain={listUser.length ? 'primary' : 'disabled'}
|
|
202
|
+
/>
|
|
203
|
+
</View>
|
|
204
|
+
</View>
|
|
205
|
+
);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export default ReceiverSelect;
|
|
@@ -42,7 +42,7 @@ const SetupScriptEmail = ({ route }) => {
|
|
|
42
42
|
}, [navigate, automate, initialUnitId, formData]);
|
|
43
43
|
|
|
44
44
|
const canSave = useMemo(() => {
|
|
45
|
-
const { title, message } = formData
|
|
45
|
+
const { title, message } = formData;
|
|
46
46
|
return !!title && !!message;
|
|
47
47
|
}, [formData]);
|
|
48
48
|
|
|
@@ -8,53 +8,43 @@ import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
|
8
8
|
import _TextInput from '../../../commons/Form/TextInput';
|
|
9
9
|
import AccessibilityLabel from '../../../configs/AccessibilityLabel';
|
|
10
10
|
import BottomButtonView from '../../../commons/BottomButtonView';
|
|
11
|
-
import { axiosPost } from '../../../utils/Apis/axios';
|
|
12
|
-
import { API } from '../../../configs';
|
|
13
|
-
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
14
11
|
import Routes from '../../../utils/Route';
|
|
15
|
-
import moment from 'moment';
|
|
16
12
|
|
|
17
13
|
const SetupScriptNotify = ({ route }) => {
|
|
18
14
|
const t = useTranslations();
|
|
19
15
|
const { goBack, navigate } = useNavigation();
|
|
20
|
-
const { automate
|
|
21
|
-
const
|
|
22
|
-
|
|
16
|
+
const { automate, unitId, multiUnit } = route.params || {};
|
|
17
|
+
const initialUnitId = useMemo(
|
|
18
|
+
() => unitId || multiUnit.id,
|
|
19
|
+
[unitId, multiUnit?.id]
|
|
20
|
+
);
|
|
21
|
+
const [formData, setFormData] = useState({ unit: initialUnitId });
|
|
23
22
|
|
|
24
23
|
const onChangeTitle = (value) => {
|
|
25
|
-
|
|
24
|
+
setFormData((state) => ({
|
|
26
25
|
...state,
|
|
27
26
|
title: value,
|
|
28
27
|
}));
|
|
29
28
|
};
|
|
30
29
|
const onChangeMessage = (value) => {
|
|
31
|
-
|
|
30
|
+
setFormData((state) => ({
|
|
32
31
|
...state,
|
|
33
32
|
message: value,
|
|
34
33
|
}));
|
|
35
34
|
};
|
|
36
35
|
|
|
37
36
|
const onNext = useCallback(async () => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
navigate({
|
|
45
|
-
name: Routes.ScriptDetail,
|
|
46
|
-
merge: true,
|
|
47
|
-
params: { saveAt: moment().valueOf() },
|
|
48
|
-
});
|
|
49
|
-
} else {
|
|
50
|
-
ToastBottomHelper.error(t('error_please_try_later'));
|
|
51
|
-
}
|
|
52
|
-
}, [automateId, navigate, notify, t]);
|
|
37
|
+
navigate(Routes.SetupScriptReceiverNotify, {
|
|
38
|
+
automate,
|
|
39
|
+
unitId: initialUnitId,
|
|
40
|
+
formData,
|
|
41
|
+
});
|
|
42
|
+
}, [navigate, automate, initialUnitId, formData]);
|
|
53
43
|
|
|
54
44
|
const canSave = useMemo(() => {
|
|
55
|
-
const { title, message } =
|
|
45
|
+
const { title, message } = formData;
|
|
56
46
|
return !!title && !!message;
|
|
57
|
-
}, [
|
|
47
|
+
}, [formData]);
|
|
58
48
|
|
|
59
49
|
return (
|
|
60
50
|
<View style={styles.wrap}>
|
|
@@ -65,7 +55,7 @@ const SetupScriptNotify = ({ route }) => {
|
|
|
65
55
|
placeholder={t('title_notification')}
|
|
66
56
|
onChange={onChangeTitle}
|
|
67
57
|
textInputStyle={styles.textTitle}
|
|
68
|
-
value={
|
|
58
|
+
value={formData.title}
|
|
69
59
|
accessibilityLabel={AccessibilityLabel.AUTOMATE_TITLE_NOTIFY}
|
|
70
60
|
autoFocus
|
|
71
61
|
/>
|
|
@@ -73,7 +63,7 @@ const SetupScriptNotify = ({ route }) => {
|
|
|
73
63
|
placeholder={t('message_notification')}
|
|
74
64
|
onChange={onChangeMessage}
|
|
75
65
|
textInputStyle={styles.textMessage}
|
|
76
|
-
value={
|
|
66
|
+
value={formData.message}
|
|
77
67
|
accessibilityLabel={AccessibilityLabel.AUTOMATE_MESSAGE_NOTIFY}
|
|
78
68
|
multiline={true}
|
|
79
69
|
maxLength={255}
|
|
@@ -1,41 +1,20 @@
|
|
|
1
|
-
import React, { useCallback,
|
|
2
|
-
import { FlatList, View } from 'react-native';
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
3
2
|
import { useNavigation } from '@react-navigation/native';
|
|
4
|
-
import styles from './Styles/SetupScriptReceiverEmailStyles';
|
|
5
|
-
import { CircleView, HeaderCustom, Text } from '../../../commons';
|
|
6
3
|
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
7
4
|
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import { API, Colors } from '../../../configs';
|
|
5
|
+
import { axiosPost } from '../../../utils/Apis/axios';
|
|
6
|
+
import { API } from '../../../configs';
|
|
11
7
|
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
12
8
|
import Routes from '../../../utils/Route';
|
|
9
|
+
import ReceiverSelect from './ReceiverSelect';
|
|
13
10
|
import moment from 'moment';
|
|
14
|
-
import CheckBox from '@react-native-community/checkbox';
|
|
15
|
-
import { useSCContextSelector } from '../../../context';
|
|
16
|
-
import { Image } from 'react-native';
|
|
17
11
|
|
|
18
12
|
const SetupScriptReceiverEmail = ({ route }) => {
|
|
19
13
|
const t = useTranslations();
|
|
20
|
-
const {
|
|
21
|
-
const { automate = {}, unitId, formData } = route.params
|
|
14
|
+
const { navigate } = useNavigation();
|
|
15
|
+
const { automate = {}, unitId, formData } = route.params;
|
|
22
16
|
const { id: automateId } = automate;
|
|
23
|
-
const [members, setMembers] = useState([]);
|
|
24
17
|
const [listUser, setListUser] = useState([]);
|
|
25
|
-
const currentUserId = useSCContextSelector(
|
|
26
|
-
(state) => state.auth.account.user.id
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const loadMembers = useCallback(async () => {
|
|
30
|
-
const { success, data } = await axiosGet(API.SHARE.UNITS_MEMBERS(unitId));
|
|
31
|
-
if (success) {
|
|
32
|
-
setMembers(data);
|
|
33
|
-
}
|
|
34
|
-
}, [unitId]);
|
|
35
|
-
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
loadMembers();
|
|
38
|
-
}, [loadMembers]);
|
|
39
18
|
|
|
40
19
|
const onNext = useCallback(async () => {
|
|
41
20
|
formData.receiver = listUser;
|
|
@@ -55,112 +34,26 @@ const SetupScriptReceiverEmail = ({ route }) => {
|
|
|
55
34
|
}
|
|
56
35
|
}, [automateId, formData, listUser, navigate, t]);
|
|
57
36
|
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Colors.GeekBlue3,
|
|
66
|
-
Colors.Purple3,
|
|
67
|
-
Colors.Orange3,
|
|
68
|
-
Colors.Volcano3,
|
|
69
|
-
Colors.Blue9,
|
|
70
|
-
Colors.Green3,
|
|
71
|
-
Colors.Cyan2,
|
|
72
|
-
],
|
|
73
|
-
[]
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
const onChecked = useCallback(
|
|
77
|
-
(id) => (checked) => {
|
|
78
|
-
setListUser((prevListUser) =>
|
|
79
|
-
checked
|
|
80
|
-
? [...prevListUser, id]
|
|
81
|
-
: prevListUser.filter((userId) => userId !== id)
|
|
82
|
-
);
|
|
37
|
+
const onLoadOption = useCallback(
|
|
38
|
+
(item) => {
|
|
39
|
+
return {
|
|
40
|
+
label: item.email,
|
|
41
|
+
invalidLabel: t('no_email'),
|
|
42
|
+
disabled: !item.email,
|
|
43
|
+
};
|
|
83
44
|
},
|
|
84
|
-
[]
|
|
45
|
+
[t]
|
|
85
46
|
);
|
|
86
47
|
|
|
87
|
-
const RowMember = memo(({ member, index, onValueChange }) => {
|
|
88
|
-
const { id, name, avatar, share_id, email } = member;
|
|
89
|
-
const [role, roleColor] = useMemo(() => {
|
|
90
|
-
if (!share_id) {
|
|
91
|
-
return [t('owner'), Colors.Primary];
|
|
92
|
-
}
|
|
93
|
-
if (id === currentUserId) {
|
|
94
|
-
return [t('me'), Colors.Primary];
|
|
95
|
-
}
|
|
96
|
-
return [t('member'), Colors.Gray6];
|
|
97
|
-
}, [share_id, id]);
|
|
98
|
-
|
|
99
|
-
const firstWordsInName = useMemo(() => {
|
|
100
|
-
return name.charAt();
|
|
101
|
-
}, [name]);
|
|
102
|
-
|
|
103
|
-
const circleColor = arrColor[index % arrColor.length];
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<View style={styles.rowContainer}>
|
|
107
|
-
<View style={styles.border}>
|
|
108
|
-
<CheckBox
|
|
109
|
-
disabled={!email}
|
|
110
|
-
lineWidth={4}
|
|
111
|
-
value={listUser.includes(id)}
|
|
112
|
-
onValueChange={onValueChange(id)}
|
|
113
|
-
style={styles.checkbox}
|
|
114
|
-
/>
|
|
115
|
-
<View style={styles.paddingLeft16}>
|
|
116
|
-
{avatar ? (
|
|
117
|
-
<Image source={{ uri: avatar }} style={styles.avatar} />
|
|
118
|
-
) : (
|
|
119
|
-
<CircleView size={40} backgroundColor={circleColor} center>
|
|
120
|
-
<Text color={Colors.White}>{firstWordsInName}</Text>
|
|
121
|
-
</CircleView>
|
|
122
|
-
)}
|
|
123
|
-
</View>
|
|
124
|
-
<View style={styles.paddingLeft16}>
|
|
125
|
-
<Text style={styles.titleName}>{name}</Text>
|
|
126
|
-
{email ? (
|
|
127
|
-
<Text style={styles.status}>{email}</Text>
|
|
128
|
-
) : (
|
|
129
|
-
<Text style={styles.invalid}>{t('no_email')}</Text>
|
|
130
|
-
)}
|
|
131
|
-
</View>
|
|
132
|
-
<View style={styles.endFlex}>
|
|
133
|
-
<Text style={[styles.textRole, { color: roleColor }]}>{role}</Text>
|
|
134
|
-
</View>
|
|
135
|
-
</View>
|
|
136
|
-
</View>
|
|
137
|
-
);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
48
|
return (
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
ListEmptyComponent={
|
|
150
|
-
<View style={styles.viewEmpty}>
|
|
151
|
-
<Text style={styles.textCenter}>{t('no_member')}</Text>
|
|
152
|
-
</View>
|
|
153
|
-
}
|
|
154
|
-
/>
|
|
155
|
-
<View style={styles.container}>
|
|
156
|
-
<BottomButtonView
|
|
157
|
-
style={styles.bottomButtonView}
|
|
158
|
-
mainTitle={t('done')}
|
|
159
|
-
onPressMain={onNext}
|
|
160
|
-
typeMain={canSave ? 'primary' : 'disabled'}
|
|
161
|
-
/>
|
|
162
|
-
</View>
|
|
163
|
-
</View>
|
|
49
|
+
<ReceiverSelect
|
|
50
|
+
listUser={listUser}
|
|
51
|
+
unitId={unitId}
|
|
52
|
+
title={t('notify_to')}
|
|
53
|
+
setListUser={setListUser}
|
|
54
|
+
onLoadOption={onLoadOption}
|
|
55
|
+
onNext={onNext}
|
|
56
|
+
/>
|
|
164
57
|
);
|
|
165
58
|
};
|
|
166
59
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { useNavigation } from '@react-navigation/native';
|
|
3
|
+
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
4
|
+
|
|
5
|
+
import { axiosPost } from '../../../utils/Apis/axios';
|
|
6
|
+
import { API } from '../../../configs';
|
|
7
|
+
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
8
|
+
import Routes from '../../../utils/Route';
|
|
9
|
+
import ReceiverSelect from './ReceiverSelect';
|
|
10
|
+
import moment from 'moment';
|
|
11
|
+
|
|
12
|
+
const SetupScriptReceiverNotify = ({ route }) => {
|
|
13
|
+
const t = useTranslations();
|
|
14
|
+
const { navigate } = useNavigation();
|
|
15
|
+
const { automate = {}, unitId, formData } = route.params;
|
|
16
|
+
const { id: automateId } = automate;
|
|
17
|
+
const [listUser, setListUser] = useState([]);
|
|
18
|
+
|
|
19
|
+
const onNext = useCallback(async () => {
|
|
20
|
+
formData.receiver = listUser;
|
|
21
|
+
const { success } = await axiosPost(
|
|
22
|
+
API.AUTOMATE.ADD_SCRIPT_NOTIFY(automateId),
|
|
23
|
+
formData
|
|
24
|
+
);
|
|
25
|
+
if (success) {
|
|
26
|
+
ToastBottomHelper.success(t('text_done'));
|
|
27
|
+
navigate({
|
|
28
|
+
name: Routes.ScriptDetail,
|
|
29
|
+
merge: true,
|
|
30
|
+
params: { saveAt: moment().valueOf() },
|
|
31
|
+
});
|
|
32
|
+
} else {
|
|
33
|
+
ToastBottomHelper.error(t('error_please_try_later'));
|
|
34
|
+
}
|
|
35
|
+
}, [automateId, formData, listUser, navigate, t]);
|
|
36
|
+
|
|
37
|
+
const onLoadOption = useCallback(
|
|
38
|
+
(item) => {
|
|
39
|
+
return {
|
|
40
|
+
label: item.phone_number,
|
|
41
|
+
invalidLabel: t('no_phone_number'),
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
[t]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<ReceiverSelect
|
|
49
|
+
listUser={listUser}
|
|
50
|
+
unitId={unitId}
|
|
51
|
+
title={t('notify_to')}
|
|
52
|
+
setListUser={setListUser}
|
|
53
|
+
onLoadOption={onLoadOption}
|
|
54
|
+
onNext={onNext}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default SetupScriptReceiverNotify;
|