@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.
Files changed (74) hide show
  1. package/index.js +2 -0
  2. package/package.json +2 -1
  3. package/src/commons/Dashboard/MyDashboardDevice/__test__/index.test.js +68 -0
  4. package/src/commons/Dashboard/MyDashboardDevice/index.js +46 -11
  5. package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +43 -11
  6. package/src/commons/Dashboard/MyUnit/index.js +40 -32
  7. package/src/commons/ModalAlert/index.js +51 -0
  8. package/src/commons/ModalAlert/styles.js +54 -0
  9. package/src/commons/SubUnit/ShortDetail.js +20 -4
  10. package/src/commons/SubUnit/__test__/ShortDetail.test.js +46 -1
  11. package/src/configs/API.js +6 -0
  12. package/src/configs/AccessibilityLabel.js +1 -0
  13. package/src/configs/Constants.js +7 -0
  14. package/src/configs/SCConfig.js +6 -0
  15. package/src/context/SCContext.tsx +12 -1
  16. package/src/context/SCStore.ts +14 -0
  17. package/src/context/actionType.ts +10 -0
  18. package/src/context/mockStore.ts +30 -1
  19. package/src/context/reducer.ts +35 -0
  20. package/src/hooks/IoT/useRemoteControl.js +4 -1
  21. package/src/hooks/IoT/useWatchSharedChips.js +130 -0
  22. package/src/hooks/Review/__test__/useInAppReview.test.js +99 -0
  23. package/src/hooks/Review/useInAppReview.js +70 -0
  24. package/src/hooks/useMqtt.js +78 -27
  25. package/src/iot/Monitor.js +149 -26
  26. package/src/iot/UpdateStates.js +60 -0
  27. package/src/iot/mqtt.js +177 -22
  28. package/src/navigations/UnitStack.js +16 -0
  29. package/src/screens/ActivityLog/FilterPopup.js +4 -79
  30. package/src/screens/ActivityLog/ItemLog.js +1 -0
  31. package/src/screens/ActivityLog/__test__/FilterPopup.test.js +2 -6
  32. package/src/screens/ActivityLog/__test__/index.test.js +51 -29
  33. package/src/screens/ActivityLog/index.js +0 -1
  34. package/src/screens/ActivityLog/styles/filterPopupStyles.js +5 -2
  35. package/src/screens/AddNewGateway/RenameNewDevices.js +5 -0
  36. package/src/screens/AddNewGateway/__test__/RenameNewDevices.test.js +18 -0
  37. package/src/screens/Automate/AddNewAction/ReceiverSelect.js +208 -0
  38. package/src/screens/Automate/AddNewAction/SetupScriptEmail.js +1 -1
  39. package/src/screens/Automate/AddNewAction/SetupScriptNotify.js +18 -28
  40. package/src/screens/Automate/AddNewAction/SetupScriptReceiverEmail.js +22 -129
  41. package/src/screens/Automate/AddNewAction/SetupScriptReceiverNotify.js +59 -0
  42. package/src/screens/Automate/AddNewAction/SetupScriptReceiverSms.js +22 -129
  43. package/src/screens/Automate/AddNewAction/SetupScriptSms.js +1 -1
  44. package/src/screens/Automate/AddNewAction/Styles/{SetupScriptReceiverEmailStyles.js → ReceiverSelectStyles.js} +18 -1
  45. package/src/screens/Automate/AddNewAction/__test__/SetupScriptNotify.test.js +16 -33
  46. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverEmail.test.js +10 -8
  47. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverNotify.test.js +217 -0
  48. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverSms.test.js +10 -8
  49. package/src/screens/Automate/Components/InputName.js +5 -1
  50. package/src/screens/Automate/OneTap/__test__/AddNewOneTap.test.js +18 -0
  51. package/src/screens/Automate/ScriptDetail/index.js +6 -6
  52. package/src/screens/CreatePassword/__test__/index.test.js +133 -0
  53. package/src/screens/CreatePassword/index.js +134 -0
  54. package/src/screens/CreatePassword/styles.js +45 -0
  55. package/src/screens/Device/__test__/DeviceDetail-3rdparty.test.js +447 -0
  56. package/src/screens/Device/__test__/DeviceDetail-arduino.test.js +344 -0
  57. package/src/screens/Device/__test__/{mqttDetail.test.js → DeviceDetail-modbus.test.js} +287 -320
  58. package/src/screens/Device/__test__/DeviceDetail-zigbee.test.js +451 -0
  59. package/src/screens/Device/__test__/DeviceDetail.test.js +502 -0
  60. package/src/screens/Device/__test__/detail.test.js +61 -3
  61. package/src/screens/Device/__test__/sensorDisplayItem.test.js +28 -3
  62. package/src/screens/Device/components/SensorDisplayItem.js +16 -14
  63. package/src/screens/Device/detail.js +14 -6
  64. package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +3 -2
  65. package/src/screens/Device/styles.js +0 -5
  66. package/src/screens/EnterPassword/__test__/EnterPassword.test.js +76 -1
  67. package/src/screens/EnterPassword/index.js +34 -4
  68. package/src/screens/EnterPassword/styles.js +1 -1
  69. package/src/utils/FactoryGateway.js +597 -0
  70. package/src/utils/I18n/translations/en.js +10 -0
  71. package/src/utils/I18n/translations/vi.js +10 -0
  72. package/src/utils/Route/index.js +3 -1
  73. package/src/utils/Validation.js +5 -0
  74. 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: 16,
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: 24,
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 = {}, unitId, multiUnit } = route.params || {};
21
- const { id: automateId } = automate;
22
- const [notify, setNotify] = useState({ unit: unitId || multiUnit.id });
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
- setNotify((state) => ({
24
+ setFormData((state) => ({
26
25
  ...state,
27
26
  title: value,
28
27
  }));
29
28
  };
30
29
  const onChangeMessage = (value) => {
31
- setNotify((state) => ({
30
+ setFormData((state) => ({
32
31
  ...state,
33
32
  message: value,
34
33
  }));
35
34
  };
36
35
 
37
36
  const onNext = useCallback(async () => {
38
- const { success } = await axiosPost(
39
- API.AUTOMATE.ADD_SCRIPT_NOTIFY(automateId),
40
- notify
41
- );
42
- if (success) {
43
- ToastBottomHelper.success(t('text_done'));
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 } = notify || {};
45
+ const { title, message } = formData;
56
46
  return !!title && !!message;
57
- }, [notify]);
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={notify?.title}
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={notify?.message}
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, useMemo, useState, useEffect, memo } from 'react';
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 BottomButtonView from '../../../commons/BottomButtonView';
9
- import { axiosPost, axiosGet } from '../../../utils/Apis/axios';
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 { goBack, navigate } = useNavigation();
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 canSave = useMemo(() => {
59
- const { title, message } = formData;
60
- return !!title && !!message && !!listUser.length;
61
- }, [formData, listUser.length]);
62
-
63
- const arrColor = useMemo(
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
- <View style={styles.wrap}>
142
- <HeaderCustom isShowClose onClose={goBack} title={t('email_to')} />
143
- <FlatList
144
- data={members}
145
- renderItem={({ item, index }) => (
146
- <RowMember member={item} index={index} onValueChange={onChecked} />
147
- )}
148
- keyExtractor={(item) => item.id.toString()}
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;