@eohjsc/react-native-smart-city 0.2.93 → 0.2.96

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 (65) hide show
  1. package/assets/images/Device/air-dry.svg +3 -0
  2. package/assets/images/Device/auto.svg +3 -0
  3. package/assets/images/Device/circulator.svg +4 -0
  4. package/assets/images/Device/clean.svg +5 -0
  5. package/package.json +2 -2
  6. package/src/Images/Common/member.svg +4 -0
  7. package/src/Images/Common/owner.svg +3 -0
  8. package/src/commons/ActionGroup/NumberUpDownActionTemplate.js +5 -1
  9. package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +5 -1
  10. package/src/commons/ActionGroup/StatesGridActionTemplate.js +42 -36
  11. package/src/commons/ActionGroup/StatesGridActionTemplateStyle.js +5 -10
  12. package/src/commons/ActionGroup/__test__/OptionsDropdownTemplate.test.js +2 -2
  13. package/src/commons/Connecting /__test__/Connecting.test.js +23 -0
  14. package/src/commons/Connecting /index.js +67 -0
  15. package/src/commons/Connecting /styles.js +28 -0
  16. package/src/commons/ConnectingProcess/index.js +3 -54
  17. package/src/commons/Explore/SearchBox/__test__/SearchBox.test.js +19 -0
  18. package/src/commons/Explore/SearchBox/index.js +2 -0
  19. package/src/commons/MenuActionMore/index.js +10 -3
  20. package/src/commons/Modal/ModalCustom.js +9 -1
  21. package/src/commons/Sharing/MemberList.js +5 -10
  22. package/src/commons/Sharing/RowMember.js +128 -38
  23. package/src/commons/Sharing/__test__/MemberList.test.js +3 -3
  24. package/src/commons/Sharing/__test__/RowMember.test.js +4 -4
  25. package/src/configs/API.js +12 -2
  26. package/src/configs/Colors.js +1 -0
  27. package/src/configs/Constants.js +13 -0
  28. package/src/hooks/Common/useSensorsStatus.js +33 -23
  29. package/src/navigations/UnitStack.js +16 -0
  30. package/src/screens/AddCommon/SelectSubUnit.js +1 -0
  31. package/src/screens/AddCommon/__test__/SelectSubUnit.test.js +2 -0
  32. package/src/screens/AddNewGateway/PlugAndPlay/ConnectWifiWarning.js +12 -3
  33. package/src/screens/AddNewGateway/PlugAndPlay/FirstWarning.js +5 -1
  34. package/src/screens/AddNewGateway/PlugAndPlay/GatewayWifiList.js +9 -1
  35. package/src/screens/EditActionsList/Styles/indexStyles.js +1 -0
  36. package/src/screens/EmergencyContacts/EmergencyContactsList.js +1 -1
  37. package/src/screens/EmergencyContacts/EmergencyContactsSelectContacts.js +29 -11
  38. package/src/screens/EmergencyContacts/__test__/EmergencyContactList.test.js +1 -0
  39. package/src/screens/EmergencyContacts/__test__/EmergencyContactsSelectContacts.test.js +56 -0
  40. package/src/screens/EnterPassword/__test__/EnterPassword.test.js +124 -0
  41. package/src/screens/EnterPassword/index.js +84 -0
  42. package/src/screens/EnterPassword/styles.js +36 -0
  43. package/src/screens/MoveToAnotherSubUnit/index.js +3 -1
  44. package/src/screens/Sharing/Components/ItemChangeRole.js +43 -0
  45. package/src/screens/Sharing/Components/SensorItem.js +7 -1
  46. package/src/screens/Sharing/Components/Styles/ItemChangeRoleStyles.js +35 -0
  47. package/src/screens/Sharing/Components/__test__/ItemChangeRole.test.js +37 -0
  48. package/src/screens/Sharing/Components/__test__/SensorItem.test.js +53 -0
  49. package/src/screens/Sharing/InfoMemberUnit.js +274 -0
  50. package/src/screens/Sharing/MemberList.js +50 -53
  51. package/src/screens/Sharing/SelectPermission.js +93 -12
  52. package/src/screens/Sharing/Styles/inforMemberUnitStyles.js +92 -0
  53. package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +121 -0
  54. package/src/screens/Sharing/__test__/MemberList.test.js +9 -24
  55. package/src/screens/Sharing/__test__/SelectPermission.test.js +53 -0
  56. package/src/screens/Sharing/hooks/index.js +76 -32
  57. package/src/screens/Unit/Detail.js +12 -10
  58. package/src/screens/Unit/ManageUnit.js +5 -5
  59. package/src/screens/Unit/ManageUnitStyles.js +1 -0
  60. package/src/screens/UnitSummary/components/3PPowerConsumption/index.js +1 -1
  61. package/src/screens/UnitSummary/components/PowerConsumption/index.js +1 -1
  62. package/src/utils/I18n/translations/en.json +14 -2
  63. package/src/utils/I18n/translations/vi.json +11 -1
  64. package/src/utils/Route/index.js +2 -0
  65. package/src/utils/Utils.js +21 -0
@@ -1,13 +1,12 @@
1
1
  import React, { memo, useMemo } from 'react';
2
- import { StyleSheet, View } from 'react-native';
3
- import { IconOutline } from '@ant-design/icons-react-native';
2
+ import { StyleSheet, View, TouchableOpacity, Image } from 'react-native';
4
3
  import { useTranslations } from '../../hooks/Common/useTranslations';
5
-
6
4
  import { Colors } from '../../configs';
7
5
  import Text from '../../commons/Text';
8
6
  import { shortEmailName } from '../../utils/Utils';
9
-
10
- import BtnRemoveMember from './BtnRemoveMember';
7
+ import { CircleView } from '../CircleView';
8
+ import { useNavigation } from '@react-navigation/native';
9
+ import Routes from '../../utils/Route';
11
10
 
12
11
  const arrColor = [
13
12
  Colors.GeekBlue3,
@@ -19,46 +18,88 @@ const arrColor = [
19
18
  Colors.Cyan2,
20
19
  ];
21
20
  const RowMember = memo(
22
- ({ member, index, ownerId, currentUserId, onPressRemove }) => {
21
+ ({
22
+ member,
23
+ index,
24
+ ownerId,
25
+ unit,
26
+ currentUserId,
27
+ type,
28
+ leftIcon = true,
29
+ rightComponent = true,
30
+ }) => {
23
31
  const t = useTranslations();
24
- const canRemoveMember = useMemo(
25
- () => ownerId === currentUserId && member.id !== ownerId,
26
- [currentUserId, member.id, ownerId]
27
- );
32
+ const { navigate } = useNavigation();
28
33
  const [role, roleColor] = useMemo(
29
34
  () =>
30
- member.id === ownerId
35
+ member?.id === ownerId
31
36
  ? [t('owner'), Colors.Primary]
32
- : member.id === currentUserId
33
- ? [t('me'), Colors.Gray6]
34
- : ['', ''],
35
- [currentUserId, member.id, ownerId, t]
37
+ : member?.id === currentUserId
38
+ ? [t('me'), Colors.Primary]
39
+ : [t('member'), Colors.Gray6],
40
+ [currentUserId, member?.id, ownerId, t]
36
41
  );
37
- const paddingBottom = role ? 16 : 23;
42
+ const firstWordsInName = useMemo(() => {
43
+ const wordTemp = member?.name || shortEmailName(member?.email) || '';
44
+ return wordTemp?.charAt();
45
+ }, [member?.email, member?.name]);
46
+
38
47
  if (member?.id === ownerId && member?.share_id) {
39
48
  return null;
40
49
  }
50
+ const circleColorTypes = {
51
+ primary: 'primary',
52
+ disable: 'disable',
53
+ noneBG: 'none',
54
+ };
55
+ const circleColor = type
56
+ ? circleColorTypes[type]
57
+ : arrColor[index % arrColor.length];
58
+
59
+ const onPressInfo = () => {
60
+ navigate(Routes.UnitMemberInformation, {
61
+ member,
62
+ ownerId,
63
+ unit,
64
+ });
65
+ };
41
66
  return (
42
67
  <View style={styles.rowContainer}>
43
- <View
44
- style={[
45
- styles.iconContainer,
46
- { backgroundColor: arrColor[index % arrColor.length] },
47
- ]}
48
- >
49
- <IconOutline name={'user'} size={20} color={Colors.White} />
50
- </View>
51
- <View style={[styles.infoContainer, { paddingBottom: paddingBottom }]}>
52
- <Text style={styles.textName}>
53
- {member?.name || shortEmailName(member.email) || ''}
54
- </Text>
55
- {!!role && (
56
- <Text style={[styles.textRole, { color: roleColor }]}>{role}</Text>
57
- )}
58
- {canRemoveMember && (
59
- <BtnRemoveMember onPressRemove={onPressRemove} member={member} />
60
- )}
61
- </View>
68
+ <TouchableOpacity onPress={onPressInfo} disabled={type === 'disable'}>
69
+ <View style={styles.Border}>
70
+ {!!leftIcon && (
71
+ <View style={styles.paddingLeft16}>
72
+ {member?.avatar ? (
73
+ <Image
74
+ source={{ uri: member?.avatar }}
75
+ style={styles.avatar}
76
+ />
77
+ ) : (
78
+ <CircleView size={40} backgroundColor={circleColor} center>
79
+ <Text color={Colors.White}>{firstWordsInName}</Text>
80
+ </CircleView>
81
+ )}
82
+ </View>
83
+ )}
84
+ <View style={styles.columnFlex}>
85
+ <Text style={styles.titleName}>
86
+ {member?.name || shortEmailName(member?.email) || ''}
87
+ </Text>
88
+ {member?.phone_number && (
89
+ <Text style={styles.status}> {member?.phone_number}</Text>
90
+ )}
91
+ </View>
92
+ {!!rightComponent && (
93
+ <View style={styles.endFlex}>
94
+ {!!role && (
95
+ <Text style={[styles.textRole, { color: roleColor }]}>
96
+ {role}
97
+ </Text>
98
+ )}
99
+ </View>
100
+ )}
101
+ </View>
102
+ </TouchableOpacity>
62
103
  </View>
63
104
  );
64
105
  }
@@ -68,9 +109,7 @@ export default RowMember;
68
109
 
69
110
  const styles = StyleSheet.create({
70
111
  rowContainer: {
71
- flexDirection: 'row',
72
- alignItems: 'center',
73
- paddingHorizontal: 16,
112
+ flex: 1,
74
113
  },
75
114
  iconContainer: {
76
115
  alignItems: 'center',
@@ -107,4 +146,55 @@ const styles = StyleSheet.create({
107
146
  textCenter: {
108
147
  alignSelf: 'center',
109
148
  },
149
+ columnFlex: {
150
+ paddingLeft: 16,
151
+ justifyContent: 'center',
152
+ flexDirection: 'column',
153
+ },
154
+ rowFlex: {
155
+ flexDirection: 'row',
156
+ },
157
+ endFlex: {
158
+ flex: 1,
159
+ justifyContent: 'flex-start',
160
+ alignItems: 'flex-end',
161
+ paddingRight: 16,
162
+ },
163
+ Border: {
164
+ display: 'flex',
165
+ width: '100%',
166
+ borderWidth: 0,
167
+ borderColor: Colors.Gray4,
168
+ borderBottomWidth: 1,
169
+ borderStyle: 'solid',
170
+ flexDirection: 'row',
171
+ paddingTop: 16,
172
+ paddingBottom: 16,
173
+ },
174
+ titleName: {
175
+ fontSize: 16,
176
+ lineHeight: 24,
177
+ fontStyle: 'normal',
178
+ fontWeight: '400',
179
+ color: Colors.Gray9,
180
+ },
181
+ status: {
182
+ fontSize: 12,
183
+ lineHeight: 20,
184
+ fontStyle: 'normal',
185
+ fontWeight: '400',
186
+ color: Colors.Gray7,
187
+ },
188
+ container: {
189
+ paddingTop: 0,
190
+ },
191
+ paddingLeft16: {
192
+ paddingLeft: 16,
193
+ },
194
+ avatar: {
195
+ height: 40,
196
+ width: 40,
197
+ borderRadius: 40,
198
+ backgroundColor: Colors.Primary,
199
+ },
110
200
  });
@@ -27,7 +27,7 @@ describe('MemberList', () => {
27
27
  });
28
28
  const instance = tree.root;
29
29
  const textInputs = instance.findAllByType(Text);
30
- expect(textInputs.length).toBe(4);
30
+ expect(textInputs.length).toBe(5);
31
31
  });
32
32
  test('MemberList snapshot id dataMember !== ownerId', () => {
33
33
  const dataMember = [{ id: 1, name: 'CEO' }];
@@ -36,7 +36,7 @@ describe('MemberList', () => {
36
36
  });
37
37
  const instance = tree.root;
38
38
  const textInputs = instance.findAllByType(Text);
39
- expect(textInputs.length).toBe(3);
39
+ expect(textInputs.length).toBe(5);
40
40
  });
41
41
  test('MemberList snapshot id dataMember === currentUserId', () => {
42
42
  const dataMember = [{ id: 1, name: 'CEO' }];
@@ -45,7 +45,7 @@ describe('MemberList', () => {
45
45
  });
46
46
  const instance = tree.root;
47
47
  const textInputs = instance.findAllByType(Text);
48
- expect(textInputs.length).toBe(4);
48
+ expect(textInputs.length).toBe(5);
49
49
  });
50
50
  test('MemberList dataMember null', () => {
51
51
  const dataMember = [];
@@ -26,8 +26,8 @@ describe('RowMember', () => {
26
26
  });
27
27
  const instance = tree.root;
28
28
  const textInputs = instance.findAllByType(Text);
29
- expect(textInputs.length).toBe(4);
30
- expect(textInputs[0].props.children).toEqual('CEO');
29
+ expect(textInputs.length).toBe(5);
30
+ expect(textInputs[1].props.children).toEqual('CEO');
31
31
  });
32
32
  test('RowMember owner dont have name show start of email ', () => {
33
33
  const dataMember = { id: 1, name: '', email: 'abc@gmail.com' };
@@ -36,7 +36,7 @@ describe('RowMember', () => {
36
36
  });
37
37
  const instance = tree.root;
38
38
  const textInputs = instance.findAllByType(Text);
39
- expect(textInputs.length).toBe(4);
40
- expect(textInputs[0].props.children).toEqual('abc');
39
+ expect(textInputs.length).toBe(5);
40
+ expect(textInputs[1].props.children).toEqual('abc');
41
41
  });
42
42
  });
@@ -39,6 +39,8 @@ const API = {
39
39
  SCConfig.apiRoot + `/property_manager/units/${id}/add_gateway/`,
40
40
  SENSORS_STATUS: (id) =>
41
41
  SCConfig.apiRoot + `/property_manager/units/${id}/sensors_status/`,
42
+ CHANGE_OWNER: (id) =>
43
+ SCConfig.apiRoot + `/property_manager/units/${id}/change_owner/`,
42
44
  FAVOURITE_DEVICES: (id) =>
43
45
  SCConfig.apiRoot + `/property_manager/units/${id}/favourite_devices/`,
44
46
  },
@@ -137,9 +139,9 @@ const API = {
137
139
  CHECK_SEND_EMAIL: () =>
138
140
  SCConfig.apiRoot + '/connection_manager/googlehome/check_send_email/',
139
141
  },
140
- POWER_CONSUME: {
142
+ VALUE_CONSUME: {
141
143
  DISPLAY_HISTORY: () =>
142
- SCConfig.apiRoot + '/property_manager/power_consume/display_history/',
144
+ SCConfig.apiRoot + '/property_manager/value_consume/display_history/',
143
145
  },
144
146
  SHARE: {
145
147
  UNITS: () => SCConfig.apiRoot + '/property_manager/sharing/units/',
@@ -150,6 +152,12 @@ const API = {
150
152
  UNITS_MEMBER_DETAIL: (id, shareId) =>
151
153
  SCConfig.apiRoot +
152
154
  `/property_manager/sharing/units/${id}/members/${shareId}/`,
155
+ UNIT_MEMBER_INFO: (unit_id, member_id) =>
156
+ SCConfig.apiRoot +
157
+ `/property_manager/sharing/units/${unit_id}/member/${member_id}/information/`,
158
+ UNIT_MEMBER_SHARE_DEVICE: (unit_id, member_id) =>
159
+ SCConfig.apiRoot +
160
+ `/property_manager/sharing/units/${unit_id}/member/${member_id}/shared_devices/`,
153
161
  SHARE: () => SCConfig.apiRoot + '/property_manager/sharing/share/',
154
162
  },
155
163
  EMERGENCY: {
@@ -157,6 +165,8 @@ const API = {
157
165
  },
158
166
  EMERGENCY_BUTTON: {
159
167
  CREATE_CONTACT: () => SCConfig.apiRoot + '/emergency_button/contacts/',
168
+ CREATE_BATCH: () =>
169
+ SCConfig.apiRoot + '/emergency_button/contacts/create_batch/',
160
170
  CONTACTS: () => SCConfig.apiRoot + '/emergency_button/contacts/',
161
171
  REMOVE_CONTACTS: (id) =>
162
172
  SCConfig.apiRoot + `/emergency_button/contacts/${id}/`,
@@ -126,6 +126,7 @@ export const Colors = {
126
126
  BlackTransparent1: 'rgba(0, 0, 0, 0.1)',
127
127
  BlackTransparent2: 'rgba(0, 0, 0, 0.2)',
128
128
  BlackTransparent3: 'rgba(0, 0, 0, 0.3)',
129
+ BlackTransparent3_5: 'rgba(0, 0, 0, 0.35)',
129
130
  BlackTransparent4: 'rgba(0, 0, 0, 0.4)',
130
131
  BlackTransparent5: 'rgba(0, 0, 0, 0.5)',
131
132
  BlackTransparent6: 'rgba(0, 0, 0, 0.6)',
@@ -217,6 +217,10 @@ export const TESTID = {
217
217
  MY_UNIT_NO_UNIT: 'MY_UNIT_NO_UNIT',
218
218
  ITEM_UNIT: 'ITEM_UNIT',
219
219
 
220
+ //member list
221
+ SHARING_MEMBER: 'SHARING_MEMBER',
222
+ REMOVE_MEMBER: 'REMOVE_MEMBER',
223
+
220
224
  // SmartTiviTemplate
221
225
  SMART_TIVI_TEMPLATE: {
222
226
  BIG_BUTTON: 'BIG_BUTTON',
@@ -309,6 +313,10 @@ export const TESTID = {
309
313
  BUTTON_SELECT_VISA: 'BUTTON_SELECT_VISA',
310
314
  BUTTON_SELECT_MASTER_CARD: 'BUTTON_SELECT_MASTER_CARD',
311
315
 
316
+ //Enter Password
317
+ ENTER_PASSWORD_TEXT_INPUT_PASSWORD: 'ENTER_PASSWORD_TEXT_INPUT_PASSWORD',
318
+ ENTER_PASSWORD_BUTTON_DONE: 'ENTER_PASSWORD_BUTTON_DONE',
319
+
312
320
  // Map Dashboard
313
321
  PARKING_MARKER: 'PARKING_MARKER',
314
322
 
@@ -424,6 +432,8 @@ export const TESTID = {
424
432
  ITEM_QUICK_ACTION_PRESS: 'ITEM_QUICK_ACTION_PRESS',
425
433
  TIME_COUNT_DOWN_TEXT: 'TIME_COUNT_DOWN_TEXT',
426
434
  ACTION_ITEM: 'ACTION_ITEM',
435
+ // Sensor Item
436
+ TEXT_SENSOR_ITEM: 'TEXT_SENSOR_ITEM',
427
437
 
428
438
  // DeviceInfo
429
439
  DEVICE_INFO_BATTERY: 'DEVICE_INFO_BATTERY',
@@ -687,6 +697,9 @@ export const TESTID = {
687
697
  BUTTON_YOUR_LOCATION: 'BUTTON_YOUR_LOCATION',
688
698
  BUTTON_CHOOSE_ON_MAP: 'BUTTON_CHOOSE_ON_MAP',
689
699
 
700
+ //searchBox
701
+ ON_FOCUS_SEARCH_B0X: 'ON_FOCUS_SEARCH_B0X',
702
+
690
703
  // MoveToAnotherSubUnit
691
704
  ROW_SUB_UNIT: 'ROW_SUB_UNIT',
692
705
 
@@ -1,5 +1,6 @@
1
- import { useState, useEffect, useRef } from 'react';
1
+ import { useState, useEffect, useRef, useCallback } from 'react';
2
2
  import { useIsFocused } from '@react-navigation/native';
3
+ import { throttle } from 'lodash';
3
4
  import { axiosGet } from '../../utils/Apis/axios';
4
5
  import { API } from '../../configs';
5
6
 
@@ -9,35 +10,44 @@ const useSensorsStatus = (unit, sensors) => {
9
10
  const [sensorsStatus, setSensorsStatus] = useState([]);
10
11
  const [serverDown, setServerDown] = useState(false);
11
12
 
12
- useEffect(() => {
13
- if (isFocused) {
14
- const getSensorsStatus = async () => {
15
- const params = new URLSearchParams();
16
- if (sensors?.length > 0) {
17
- sensors.forEach((sensor) => {
18
- params.append('sensors', sensor.id);
19
- });
20
- const { success, data, resp_status } = await axiosGet(
21
- API.UNIT.SENSORS_STATUS(unit.id),
22
- {
23
- params: params,
24
- }
25
- );
26
- if (success) {
27
- setSensorsStatus(data);
28
- setServerDown(false);
29
- } else if (resp_status >= 500) {
30
- setServerDown(true);
13
+ // eslint-disable-next-line react-hooks/exhaustive-deps
14
+ const getSensorsStatus = useCallback(
15
+ throttle(async (unit, sensors) => {
16
+ const params = new URLSearchParams();
17
+ if (sensors?.length > 0) {
18
+ sensors.forEach((sensor) => {
19
+ params.append('sensors', sensor.id);
20
+ });
21
+ const { success, data, resp_status } = await axiosGet(
22
+ API.UNIT.SENSORS_STATUS(unit.id),
23
+ {
24
+ params: params,
31
25
  }
26
+ );
27
+ if (success) {
28
+ setSensorsStatus(data);
29
+ setServerDown(false);
30
+ } else if (resp_status > 500) {
31
+ setServerDown(true);
32
32
  }
33
- };
34
- const updateInterval = setInterval(getSensorsStatus, 5000);
33
+ }
34
+ }, 3000),
35
+ []
36
+ );
37
+
38
+ useEffect(() => {
39
+ if (isFocused) {
40
+ getSensorsStatus(unit, sensors);
41
+ const updateInterval = setInterval(
42
+ () => getSensorsStatus(unit, sensors),
43
+ 5000
44
+ );
35
45
  intervalSensorStatus.current = updateInterval;
36
46
  return () => clearInterval(updateInterval);
37
47
  } else {
38
48
  clearInterval(intervalSensorStatus.current);
39
49
  }
40
- }, [isFocused, sensors, unit.id]);
50
+ }, [isFocused, getSensorsStatus, unit, sensors]);
41
51
 
42
52
  const getStatus = (sensor) => {
43
53
  sensorsStatus.find((s) => s.id === sensor.id);
@@ -49,6 +49,8 @@ import SetUpSensor from '../screens/AddNewAction/SetupSensor';
49
49
  import EditDevice from '../screens/Device/EditDevice/index';
50
50
  import EmergencySetting from '../screens/EmergencySetting';
51
51
  import ConfirmUnitDeletion from '../screens/ConfirmUnitDeletion';
52
+ import InfoMemberUnit from '../screens/Sharing/InfoMemberUnit';
53
+ import EnterPassword from '../screens/EnterPassword';
52
54
  import { HanetCameraStack } from './HanetCameraStack';
53
55
 
54
56
  import { axiosGet } from '../utils/Apis/axios';
@@ -268,6 +270,20 @@ export const UnitStack = memo((props) => {
268
270
  headerShown: false,
269
271
  }}
270
272
  />
273
+ <Stack.Screen
274
+ name={Route.UnitMemberInformation}
275
+ component={InfoMemberUnit}
276
+ options={{
277
+ headerShown: false,
278
+ }}
279
+ />
280
+ <Stack.Screen
281
+ name={Route.EnterPassword}
282
+ component={EnterPassword}
283
+ options={{
284
+ headerShown: false,
285
+ }}
286
+ />
271
287
  <Stack.Screen
272
288
  name={Route.DeviceInfo}
273
289
  component={DeviceInfo}
@@ -85,6 +85,7 @@ const AddCommonSelectSubUnit = ({ route }) => {
85
85
  station: subUnits[selectedIndex]?.id,
86
86
  unit_name: unit?.name,
87
87
  ...route.params,
88
+ isNewFlow: false,
88
89
  });
89
90
  break;
90
91
  case 'AddHassiDevice':
@@ -133,6 +133,8 @@ describe('Test SelectSubUnit container', () => {
133
133
  expect(mockedNavigate).toBeCalledWith('ScanChipQR', {
134
134
  addType: 'AddNewGateway',
135
135
  station: undefined,
136
+ isNewFlow: false,
137
+ unit_name: undefined,
136
138
  });
137
139
  break;
138
140
  case 'AddHassiDevice':
@@ -11,7 +11,6 @@ import {
11
11
  Text,
12
12
  PermissionsAndroid,
13
13
  Platform,
14
- ActivityIndicator,
15
14
  } from 'react-native';
16
15
  import WifiManager from 'react-native-wifi-reborn';
17
16
  import { useNavigation } from '@react-navigation/native';
@@ -27,6 +26,7 @@ import Routes from '../../../utils/Route';
27
26
  import { SCContext } from '../../../context';
28
27
  import { Action } from '../../../context/actionType';
29
28
  import { TESTID } from '../../../configs/Constants';
29
+ import Connecting from '../../../commons/Connecting ';
30
30
 
31
31
  const isIos = Platform.OS === 'ios';
32
32
  const isAndroid = Platform.OS === 'android';
@@ -50,6 +50,9 @@ const ConnectWifiWarning = memo(({ route }) => {
50
50
  const t = useTranslations();
51
51
  const { navigate, goBack } = useNavigation();
52
52
  const [isLoading, setIsLoading] = useState(false);
53
+ const [isConnect, setIsConnect] = useState(false);
54
+ const [isPercentConnect, setIsPercentConnect] = useState(0);
55
+
53
56
  const { setAction } = useContext(SCContext);
54
57
 
55
58
  const getPermissionWifiAndroid = useCallback(async () => {
@@ -76,6 +79,7 @@ const ConnectWifiWarning = memo(({ route }) => {
76
79
  const handleSend = async () => {
77
80
  let intervalSend = null;
78
81
  if (isIos) {
82
+ await setIsPercentConnect(1);
79
83
  await socket.on('message', (msg, rinfo) => {
80
84
  clearInterval(intervalSend);
81
85
  const data = JSON.parse(msg.toString());
@@ -110,6 +114,7 @@ const ConnectWifiWarning = memo(({ route }) => {
110
114
  });
111
115
  } else {
112
116
  const socket = dgram.createSocket({ type: 'udp4' });
117
+ await setIsPercentConnect(1);
113
118
  await socket.bind(54321);
114
119
  await socket.on('message', (msg, rinfo) => {
115
120
  const data = JSON.parse(msg.toString());
@@ -147,6 +152,7 @@ const ConnectWifiWarning = memo(({ route }) => {
147
152
 
148
153
  const handleConnectWifiGateway = async () => {
149
154
  setIsLoading(true);
155
+ setIsConnect(true);
150
156
  // eslint-disable-next-line promise/prefer-await-to-then
151
157
  WifiManager.connectToProtectedSSID(wifi_ssid, wifi_pass, false).then(
152
158
  () => {
@@ -185,8 +191,11 @@ const ConnectWifiWarning = memo(({ route }) => {
185
191
  </>
186
192
  ) : (
187
193
  <View style={styles.centerLoading}>
188
- <ActivityIndicator color={Colors.Primary} size={'small'} />
189
- <Text>{t('connecting')}</Text>
194
+ <Connecting
195
+ isLoading={isLoading}
196
+ isConnect={isConnect}
197
+ isPercentConnect={isPercentConnect}
198
+ />
190
199
  </View>
191
200
  )}
192
201
  </View>
@@ -37,10 +37,11 @@ const FirstWarning = memo(({ route }) => {
37
37
  </View>
38
38
  </View>
39
39
  <ViewButtonBottom
40
- leftTitle={t('text_back')}
40
+ leftTitle={t('cancel')}
41
41
  onLeftClick={goBack}
42
42
  rightTitle={t('text_next')}
43
43
  onRightClick={onRight}
44
+ styleButtonLeftText={styles.buttonLeftText}
44
45
  />
45
46
  </View>
46
47
  );
@@ -72,4 +73,7 @@ const styles = StyleSheet.create({
72
73
  paddingBottom: 30,
73
74
  color: Colors.Red,
74
75
  },
76
+ buttonLeftText: {
77
+ color: Colors.Gray6,
78
+ },
75
79
  });
@@ -205,9 +205,17 @@ const GatewayWifiList = memo(({ route }) => {
205
205
  }
206
206
  }, [isSendWifi, setAction]);
207
207
 
208
+ const handleOnGoBack = useCallback(() => {
209
+ navigate(Routes.Dashboard);
210
+ }, [navigate]);
211
+
208
212
  return (
209
213
  <View style={styles.screen}>
210
- <HeaderCustom title={t('connect_device')} isShowSeparator />
214
+ <HeaderCustom
215
+ onGoBack={handleOnGoBack}
216
+ title={t('connect_home_wifi')}
217
+ isShowSeparator
218
+ />
211
219
  <View>
212
220
  <Text style={styles.title} semibold type="H3">
213
221
  {t('set_network')}
@@ -88,6 +88,7 @@ export default StyleSheet.create({
88
88
  },
89
89
  titleItem: {
90
90
  flexDirection: 'row',
91
+ width: '45%',
91
92
  },
92
93
  paddingRight4: {
93
94
  paddingRight: 4,
@@ -82,7 +82,7 @@ export const EmergencyContactsList = ({ route }) => {
82
82
  id: 2,
83
83
  text: t('select_unit_members'),
84
84
  route: Routes.EmergencyContactsSelectContacts,
85
- data: { unitId, group },
85
+ data: { unitId, group, lengthListContacts: listContacts.length },
86
86
  },
87
87
  ];
88
88
 
@@ -8,15 +8,20 @@ import { RowUser } from '../../commons/RowUser';
8
8
  import WrapHeaderScrollable from '../../commons/Sharing/WrapHeaderScrollable';
9
9
  import { API, Colors, Theme } from '../../configs';
10
10
  import { TESTID } from '../../configs/Constants';
11
+
11
12
  import { axiosGet, axiosPost } from '../../utils/Apis/axios';
12
13
  import { ToastBottomHelper } from '../../utils/Utils';
13
14
 
15
+ const MAX_EMERGENCY_CONTACTS = 5;
16
+
14
17
  export const EmergencyContactsSelectContacts = ({ route }) => {
15
18
  const t = useTranslations();
16
19
  const { goBack } = useNavigation();
17
- const { unitId, group } = route.params;
20
+ const { unitId, group, lengthListContacts } = route.params;
18
21
  const [dataContact, setDataContact] = useState([]);
22
+ const [currentContacts, setCurrentContact] = useState([]);
19
23
  const [loading, setLoading] = useState(true);
24
+ const NUMBER_EMERGENCY_CONTACTS = MAX_EMERGENCY_CONTACTS - lengthListContacts;
20
25
 
21
26
  const loadMembers = useCallback(async (id) => {
22
27
  setLoading(true);
@@ -27,22 +32,33 @@ export const EmergencyContactsSelectContacts = ({ route }) => {
27
32
  }
28
33
  }, []);
29
34
 
30
- const [currentContacts, setCurrentContact] = useState('');
31
-
32
35
  const onPressContact = useCallback(
33
- (contact) => () => {
34
- setCurrentContact(contact);
36
+ (name, phone_number) => () => {
37
+ if (currentContacts.some((contact) => contact?.name === name)) {
38
+ setCurrentContact((oldArray) =>
39
+ oldArray.filter((contact) => contact?.name !== name)
40
+ );
41
+ } else {
42
+ if (NUMBER_EMERGENCY_CONTACTS !== currentContacts.length) {
43
+ setCurrentContact((oldArray) => [
44
+ ...oldArray,
45
+ { name, phone_number },
46
+ ]);
47
+ } else {
48
+ ToastBottomHelper.error(t('maxmium_contacts'));
49
+ }
50
+ }
35
51
  },
36
- []
52
+ [NUMBER_EMERGENCY_CONTACTS, currentContacts, t]
37
53
  );
38
54
 
39
55
  const goSave = useCallback(async () => {
40
- const { success } = await axiosPost(API.EMERGENCY_BUTTON.CREATE_CONTACT(), {
56
+ const { success } = await axiosPost(API.EMERGENCY_BUTTON.CREATE_BATCH(), {
41
57
  group: group.id,
42
- phone_number: currentContacts?.phone_number,
43
- name: currentContacts?.name,
58
+ contacts: currentContacts,
44
59
  });
45
60
  if (success) {
61
+ ToastBottomHelper.success(t('saving_contact_successful'));
46
62
  goBack();
47
63
  } else {
48
64
  ToastBottomHelper.error(t('create_contact_failed'));
@@ -66,10 +82,12 @@ export const EmergencyContactsSelectContacts = ({ route }) => {
66
82
  }
67
83
  text={contact.name}
68
84
  subtext={contact.phone_number}
69
- onPress={onPressContact(contact)}
85
+ onPress={onPressContact(contact.name, contact.phone_number)}
70
86
  rightComponent={
71
87
  <View style={styles.buttonRemove}>
72
- {currentContacts?.phone_number === contact.phone_number && (
88
+ {currentContacts.some(
89
+ (item) => item?.name === contact?.name
90
+ ) && (
73
91
  <IconOutline
74
92
  name={'check-circle'}
75
93
  size={20}