@eohjsc/react-native-smart-city 0.2.45 → 0.2.49

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 (98) hide show
  1. package/README.md +9 -0
  2. package/index.js +6 -0
  3. package/package.json +23 -3
  4. package/src/Images/Common/logo.png +0 -0
  5. package/src/Images/Common/logo@2x.png +0 -0
  6. package/src/Images/Common/logo@3x.png +0 -0
  7. package/src/commons/ActionTemplate/index.js +40 -25
  8. package/src/commons/Automate/ItemAutomate.js +2 -2
  9. package/src/commons/Dashboard/MyPinnedSharedUnit/index.js +82 -0
  10. package/src/commons/Dashboard/MyPinnedSharedUnit/styles.js +78 -0
  11. package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +60 -0
  12. package/src/commons/Dashboard/MyUnit/index.js +111 -0
  13. package/src/commons/Dashboard/MyUnit/styles.js +61 -0
  14. package/src/commons/Device/ConnectedViewHeader.js +4 -1
  15. package/src/commons/Form/TextInput.js +1 -1
  16. package/src/commons/MediaPlayer/index.js +6 -1
  17. package/src/commons/OneTapTemplate/OptionsDropdownActionTemplate.js +3 -3
  18. package/src/{screens/Unit/components → commons}/SearchLocation/RowLocation.js +2 -2
  19. package/src/{screens/Unit/components → commons}/SearchLocation/RowLocationStyles.js +1 -1
  20. package/src/{screens/Unit/components → commons}/SearchLocation/SearchBarLocationStyles.js +1 -1
  21. package/src/{screens/Unit/components → commons}/SearchLocation/__test__/RowLocation.test.js +2 -2
  22. package/src/{screens/Unit/components → commons}/SearchLocation/index.js +2 -2
  23. package/src/commons/SubUnit/OneTap/ItemOneTap.js +3 -1
  24. package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +2 -0
  25. package/src/commons/SubUnit/ShortDetail.js +1 -1
  26. package/src/commons/Unit/SharedUnit.js +2 -2
  27. package/src/configs/API.js +11 -4
  28. package/src/configs/Constants.js +19 -6
  29. package/src/configs/Images.js +1 -0
  30. package/src/iot/RemoteControl/Bluetooth.js +10 -1
  31. package/src/iot/RemoteControl/Internet.js +0 -2
  32. package/src/navigations/AddDeviceStack.js +5 -0
  33. package/src/navigations/AddUnitStack.js +2 -0
  34. package/src/navigations/UnitStack.js +8 -0
  35. package/src/screens/ActivityLog/FilterPopup.js +24 -17
  36. package/src/screens/ActivityLog/ItemLog.js +26 -22
  37. package/src/screens/ActivityLog/__test__/FilterPopup.test.js +1 -0
  38. package/src/screens/ActivityLog/__test__/ItemLog.test.js +0 -20
  39. package/src/screens/ActivityLog/__test__/index.test.js +3 -0
  40. package/src/screens/ActivityLog/hooks/__test__/index.test.js +8 -0
  41. package/src/screens/ActivityLog/hooks/index.js +5 -9
  42. package/src/screens/ActivityLog/index.js +4 -4
  43. package/src/screens/ActivityLog/styles/filterPopupStyles.js +3 -0
  44. package/src/screens/AddCommon/SelectSubUnit.js +10 -1
  45. package/src/screens/AddCommon/SelectUnit.js +11 -0
  46. package/src/screens/AddLocationMaps/index.js +124 -74
  47. package/src/screens/AddLocationMaps/indexStyle.js +58 -0
  48. package/src/screens/AddNewAction/SelectAction.js +59 -4
  49. package/src/screens/AddNewAction/SelectSensorDevices.js +27 -5
  50. package/src/screens/AddNewAction/SetupSensor.js +1 -6
  51. package/src/screens/AddNewAction/__test__/SelectAction.test.js +6 -4
  52. package/src/screens/AddNewAutoSmart/__test__/AddNewAutoSmart.test.js +11 -1
  53. package/src/screens/AddNewAutoSmart/index.js +24 -13
  54. package/src/screens/AddNewDevice/ConnectingDevices.js +3 -0
  55. package/src/screens/AddNewGateway/ConnectingGatewayStyles.js +10 -1
  56. package/src/screens/AddNewOneTap/__test__/AddNewOneTap.test.js +9 -1
  57. package/src/screens/AddNewOneTap/index.js +11 -6
  58. package/src/screens/Automate/MultiUnits.js +14 -53
  59. package/src/screens/Automate/__test__/MultiUnits.test.js +5 -246
  60. package/src/screens/Automate/__test__/index.test.js +1 -0
  61. package/src/screens/Automate/index.js +1 -0
  62. package/src/screens/Device/__test__/detail.test.js +14 -0
  63. package/src/screens/Device/components/DetailHistoryChart.js +63 -0
  64. package/src/screens/Device/components/EmergencyCountdown.js +29 -0
  65. package/src/screens/Device/components/SensorConnectStatusViewHeader.js +57 -0
  66. package/src/screens/Device/components/SensorDisplayItem.js +127 -0
  67. package/src/screens/Device/detail.js +81 -294
  68. package/src/screens/Device/hooks/useDisconnectedDevice.js +63 -0
  69. package/src/screens/Device/styles.js +0 -10
  70. package/src/screens/GuestInfo/components/RecurringDetail.js +2 -2
  71. package/src/screens/Notification/components/NotificationItem.js +31 -14
  72. package/src/screens/Notification/styles/NotificationItemStyles.js +5 -0
  73. package/src/screens/ScanChipQR/__test__/ScanChipQR.test.js +12 -3
  74. package/src/screens/ScanChipQR/components/QRScan/__test__/QRScan.test.js +11 -2
  75. package/src/screens/ScanSensorQR/__test__/ScanSensorQR.test.js +12 -3
  76. package/src/screens/ScriptDetail/__test__/index.test.js +83 -3
  77. package/src/screens/ScriptDetail/hooks/index.js +4 -0
  78. package/src/screens/ScriptDetail/index.js +65 -4
  79. package/src/screens/SetSchedule/__test__/SelectWeekday.test.js +2 -2
  80. package/src/screens/SetSchedule/__test__/index.test.js +8 -0
  81. package/src/screens/SetSchedule/components/SelectWeekday.js +13 -7
  82. package/src/screens/SetSchedule/index.js +28 -6
  83. package/src/screens/Sharing/Components/__test__/TitleCheckBox.test.js +38 -0
  84. package/src/screens/SubUnit/AddSubUnit.js +113 -29
  85. package/src/screens/SubUnit/AddSubUnitStyles.js +10 -0
  86. package/src/screens/SubUnit/__test__/AddSubUnit.test.js +1 -0
  87. package/src/screens/TDSGuide/index.js +15 -19
  88. package/src/screens/Unit/MoreMenu.js +6 -1
  89. package/src/screens/Unit/SelectLocation.js +2 -3
  90. package/src/screens/Unit/SmartAccount.js +141 -0
  91. package/src/screens/Unit/SmartAccountItem.js +51 -0
  92. package/src/screens/Unit/SmartAccountStyles.js +46 -0
  93. package/src/screens/Unit/__test__/SmartAccount.test.js +58 -0
  94. package/src/screens/Unit/hook/useStateAlertRemove.js +36 -0
  95. package/src/utils/I18n/translations/en.json +26 -2
  96. package/src/utils/I18n/translations/vi.json +38 -14
  97. package/src/utils/Route/index.js +4 -0
  98. package/src/utils/Validation.js +3 -1
@@ -28,13 +28,27 @@ const getDateString = (date) => {
28
28
 
29
29
  const SetSchedule = ({ route }) => {
30
30
  const t = useTranslations();
31
- const { type, unit, isAutomateTab, isMultiUnits, automateId, scriptName } =
32
- route.params;
31
+ const {
32
+ type,
33
+ unit,
34
+ isAutomateTab,
35
+ isMultiUnits,
36
+ automateId, // wilk=l remove later
37
+ automate = {},
38
+ scriptName,
39
+ } = route.params;
33
40
  const { navigate, dispatch, goBack } = useNavigation();
34
- const [repeat, setRepeat] = useState(REPEAT_OPTIONS.ONCE);
35
- const [time, setTime] = useState(moment().second(0));
36
- const [date, setDate] = useState(moment());
37
- const [weekday, setWeekday] = useState([]);
41
+ const [repeat, setRepeat] = useState(automate.repeat || REPEAT_OPTIONS.ONCE);
42
+
43
+ const [time, setTime] = useState(
44
+ automate.time_repeat
45
+ ? moment(automate.time_repeat, 'HH:mm:ss')
46
+ : moment().second(0)
47
+ );
48
+ const [date, setDate] = useState(
49
+ automate.date_repeat ? moment(automate.date_repeat, 'YYYY-MM-DD') : moment()
50
+ );
51
+ const [weekday, setWeekday] = useState(automate.weekday_repeat || []);
38
52
 
39
53
  const [showRepeatOptions, setShowRepeatOptions, setHideRepeatOptions] =
40
54
  useBoolean();
@@ -51,6 +65,13 @@ const SetSchedule = ({ route }) => {
51
65
  date_repeat: date.format('YYYY-MM-DD'),
52
66
  weekday_repeat: weekday,
53
67
  },
68
+ automate: {
69
+ ...automate,
70
+ repeat: repeat,
71
+ time_repeat: time.format('HH:mm:ss'),
72
+ date_repeat: date.format('YYYY-MM-DD'),
73
+ weekday_repeat: weekday,
74
+ },
54
75
  isAutomateTab,
55
76
  isMultiUnits,
56
77
  automateId,
@@ -64,6 +85,7 @@ const SetSchedule = ({ route }) => {
64
85
  time,
65
86
  date,
66
87
  weekday,
88
+ automate,
67
89
  isAutomateTab,
68
90
  isMultiUnits,
69
91
  automateId,
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import { create, act } from 'react-test-renderer';
3
+
4
+ import { SCProvider } from '../../../../context';
5
+ import { mockSCStore } from '../../../../context/mockStore';
6
+ import TitleCheckBox from '../TitleCheckBox';
7
+ import { CheckBoxCustom } from '..';
8
+
9
+ jest.mock('react', () => {
10
+ return {
11
+ ...jest.requireActual('react'),
12
+ memo: (x) => x,
13
+ };
14
+ });
15
+
16
+ const mockOnPress = jest.fn();
17
+ const wrapComponent = (idGroup, id) => (
18
+ <SCProvider initState={mockSCStore({})}>
19
+ <TitleCheckBox idGroup={idGroup} id={id} onPress={mockOnPress} />
20
+ </SCProvider>
21
+ );
22
+
23
+ describe('test TitleCheckBox', () => {
24
+ test('test handleOnPress', () => {
25
+ let tree;
26
+ act(() => {
27
+ tree = create(wrapComponent(1, 2));
28
+ });
29
+ const instance = tree.root;
30
+ const checkBoxCustom = instance.findByType(CheckBoxCustom);
31
+ act(() => {
32
+ checkBoxCustom.props.onPress();
33
+ });
34
+
35
+ expect(checkBoxCustom.props.isChecked).toBe(true);
36
+ expect(mockOnPress).toHaveBeenLastCalledWith(1, true, 2);
37
+ });
38
+ });
@@ -7,6 +7,7 @@ import {
7
7
  SafeAreaView,
8
8
  } from 'react-native';
9
9
  import { useNavigation } from '@react-navigation/native';
10
+ import ImageResizer from 'react-native-image-resizer';
10
11
  import { useTranslations } from '../../hooks/Common/useTranslations';
11
12
 
12
13
  import { API, Colors } from '../../configs';
@@ -19,45 +20,103 @@ import { ToastBottomHelper } from '../../utils/Utils';
19
20
  import { TESTID } from '../../configs/Constants';
20
21
  import styles from './AddSubUnitStyles';
21
22
 
23
+ const MAX_FILE_SIZE_BYTES = 1.5 * 1024 * 1024; // 1.5mb
24
+
25
+ const prepareImageToUpload = async (image) => {
26
+ if (!image || image.fileSize < MAX_FILE_SIZE_BYTES) {
27
+ return image;
28
+ }
29
+ const result = await ImageResizer.createResizedImage(
30
+ image.uri,
31
+ 1280,
32
+ 1280,
33
+ 'JPEG',
34
+ 70
35
+ );
36
+ return {
37
+ fileName: result?.name,
38
+ type: 'image/jpeg',
39
+ uri: result?.uri,
40
+ };
41
+ };
42
+
22
43
  const AddSubUnit = ({ route }) => {
23
44
  const t = useTranslations();
24
45
  const navigation = useNavigation();
25
- const { unit, addType } = route.params;
46
+ const { unit, addType, isAddUnit, location = '' } = route?.params;
26
47
  const [roomName, setRoomName] = useState('');
27
48
  const [wallpaper, setWallpaper] = useState('');
28
49
  const [imageUrl, setImageUrl] = useState('');
29
50
  const [showImagePicker, setShowImagePicker] = useState(false);
30
51
 
31
52
  const goDone = useCallback(async () => {
32
- const dataObj = { name: roomName, background: wallpaper };
33
- const formData = createFormData(dataObj, ['background']);
34
- const { success, data } = await axiosPost(
35
- API.SUB_UNIT.CREATE_SUB_UNIT(unit.id),
36
- formData,
37
- {
38
- headers: { 'Content-Type': 'multipart/form-data' },
53
+ if (isAddUnit) {
54
+ const dataObj = {
55
+ name: roomName,
56
+ address: location,
57
+ background: wallpaper,
58
+ };
59
+ dataObj.background = await prepareImageToUpload(dataObj.background);
60
+ const formData = createFormData(dataObj, ['background']);
61
+ const { success, data } = await axiosPost(
62
+ API.UNIT.CREATE_UNIT(),
63
+ formData,
64
+ {
65
+ headers: { 'Content-Type': 'multipart/form-data' },
66
+ }
67
+ );
68
+ if (success) {
69
+ navigation.navigate(Routes.UnitStack, {
70
+ screen: Routes.UnitDetail,
71
+ params: {
72
+ unitId: data?.id,
73
+ },
74
+ });
75
+ } else {
76
+ ToastBottomHelper.error(t('text_create_unit_fail'));
39
77
  }
40
- );
41
- if (success) {
42
- ToastBottomHelper.success(t('text_create_sub_unit_success'));
78
+ } else {
79
+ const dataObj = { name: roomName, background: wallpaper };
80
+ dataObj.background = await prepareImageToUpload(dataObj.background);
81
+ const formData = createFormData(dataObj, ['background']);
82
+ const { success, data } = await axiosPost(
83
+ API.SUB_UNIT.CREATE_SUB_UNIT(unit.id),
84
+ formData,
85
+ {
86
+ headers: { 'Content-Type': 'multipart/form-data' },
87
+ }
88
+ );
89
+ if (success) {
90
+ ToastBottomHelper.success(t('text_create_sub_unit_success'));
43
91
 
44
- if (addType === 'AddNewGateway') {
45
- navigation.navigate(Routes.AddCommonSelectSubUnit, {
46
- ...route.params,
92
+ if (addType === 'AddNewGateway') {
93
+ navigation.navigate(Routes.AddCommonSelectSubUnit, {
94
+ ...route.params,
95
+ });
96
+ return;
97
+ }
98
+ navigation.navigate(Routes.UnitStack, {
99
+ screen: Routes.SubUnitDetail,
100
+ params: {
101
+ unit: unit,
102
+ station: data,
103
+ },
47
104
  });
48
- return;
105
+ } else {
106
+ ToastBottomHelper.error(t('text_create_sub_unit_fail'));
49
107
  }
50
- navigation.navigate(Routes.UnitStack, {
51
- screen: Routes.SubUnitDetail,
52
- params: {
53
- unit: unit,
54
- station: data,
55
- },
56
- });
57
- } else {
58
- ToastBottomHelper.error(t('text_create_sub_unit_fail'));
59
108
  }
60
- }, [addType, navigation, roomName, route.params, t, unit, wallpaper]);
109
+ }, [
110
+ addType,
111
+ navigation,
112
+ roomName,
113
+ route.params,
114
+ t,
115
+ unit,
116
+ wallpaper,
117
+ isAddUnit,
118
+ location,
119
+ ]);
61
120
 
62
121
  const onChoosePhoto = useCallback(() => {
63
122
  setShowImagePicker(true);
@@ -77,8 +136,16 @@ const AddSubUnit = ({ route }) => {
77
136
  );
78
137
 
79
138
  const validateData = useMemo(() => {
80
- return roomName === '' || wallpaper === '';
81
- }, [roomName, wallpaper]);
139
+ if (isAddUnit) {
140
+ return roomName === '' || wallpaper === '' || location === '';
141
+ } else {
142
+ return roomName === '' || wallpaper === '';
143
+ }
144
+ }, [roomName, wallpaper, location, isAddUnit]);
145
+
146
+ const onChooseLocation = useCallback(() => {
147
+ navigation.navigate(Routes.AddLocationMaps);
148
+ }, [navigation]);
82
149
 
83
150
  return (
84
151
  <SafeAreaView style={styles.wrap}>
@@ -90,16 +157,33 @@ const AddSubUnit = ({ route }) => {
90
157
  color={Colors.Black}
91
158
  style={[styles.title, styles.padding]}
92
159
  >
93
- {t('add_new_sub_unit')}
160
+ {isAddUnit ? t('add_new_unit') : t('add_new_sub_unit')}
94
161
  </Text>
95
162
  <View style={styles.formWrapper}>
96
163
  <_TextInput
97
- placeholder={t('add_sub_unit_room_name')}
164
+ placeholder={
165
+ isAddUnit ? t('name_your_unit') : t('add_sub_unit_room_name')
166
+ }
98
167
  onChange={onChangeRoomName}
99
168
  textInputStyle={styles.roomName}
100
169
  wrapStyle={styles.textInput}
101
170
  selectionColor={Colors.Primary}
102
171
  />
172
+
173
+ {isAddUnit && (
174
+ <TouchableWithoutFeedback onPress={onChooseLocation}>
175
+ <View style={styles.wrapGeolocation}>
176
+ <Text style={styles.addWallpaper}>{t('geolocation')}</Text>
177
+ <Text
178
+ style={styles.textLocation}
179
+ color={location && Colors.Primary}
180
+ >
181
+ {location || t('text_explain_add_geolocation')}
182
+ </Text>
183
+ </View>
184
+ </TouchableWithoutFeedback>
185
+ )}
186
+
103
187
  <TouchableWithoutFeedback
104
188
  testID={TESTID.ADD_SUB_UNIT_BUTTON_CHOOSE_PHOTO}
105
189
  onPress={onChoosePhoto}
@@ -65,4 +65,14 @@ export default StyleSheet.create({
65
65
  marginRight: 8,
66
66
  backgroundColor: Colors.Pink1,
67
67
  },
68
+ wrapGeolocation: {
69
+ marginTop: 6,
70
+ borderBottomWidth: 1,
71
+ borderColor: Colors.Gray4,
72
+ },
73
+ textLocation: {
74
+ fontSize: 16,
75
+ lineHeight: 24,
76
+ paddingBottom: 14,
77
+ },
68
78
  });
@@ -24,6 +24,7 @@ const mockedGoBack = jest.fn();
24
24
 
25
25
  jest.mock('axios');
26
26
  jest.mock('react-native-toast-message');
27
+ jest.mock('react-native-image-resizer');
27
28
 
28
29
  jest.mock('react-redux', () => {
29
30
  return {
@@ -1,7 +1,6 @@
1
1
  import React, { memo } from 'react';
2
- import { StyleSheet, SafeAreaView } from 'react-native';
2
+ import { StyleSheet, SafeAreaView, ScrollView } from 'react-native';
3
3
  import { useTranslations } from '../../hooks/Common/useTranslations';
4
- import i18n from 'i18n-js';
5
4
  import { TESTID } from '../../configs/Constants';
6
5
 
7
6
  import { Theme } from '../../configs';
@@ -11,23 +10,22 @@ import useTitleHeader from '../../hooks/Common/useTitleHeader';
11
10
  const TDSGuide = memo(() => {
12
11
  const t = useTranslations();
13
12
  useTitleHeader(t('tds_infomation'));
14
- const currentLanguage = i18n.currentLocale();
15
13
 
16
14
  return (
17
15
  <SafeAreaView style={styles.container}>
18
- <Text
19
- type="H3"
20
- semibold
21
- style={styles.titlePadding}
22
- testID={TESTID.TDS_GUIDE_TITLE}
23
- >
24
- {t('what_is_tds_title')}
25
- </Text>
26
- <Text type="Body">{t('what_is_tds_text')}</Text>
27
- <Text type="H3" semibold style={styles.titlePadding}>
28
- {t('tds_guidlines_title')}
29
- </Text>
30
- {currentLanguage === 'vi' ? (
16
+ <ScrollView>
17
+ <Text
18
+ type="H3"
19
+ semibold
20
+ style={styles.titlePadding}
21
+ testID={TESTID.TDS_GUIDE_TITLE}
22
+ >
23
+ {t('what_is_tds_title')}
24
+ </Text>
25
+ <Text type="Body">{t('what_is_tds_text')}</Text>
26
+ <Text type="H3" semibold style={styles.titlePadding}>
27
+ {t('tds_guidlines_title')}
28
+ </Text>
31
29
  <>
32
30
  <Text type="Body">
33
31
  {t('tds_guidlines_text_1')}
@@ -39,9 +37,7 @@ const TDSGuide = memo(() => {
39
37
  {t('tds_guidlines_text_3')}
40
38
  </Text>
41
39
  </>
42
- ) : (
43
- <Text type="Body">{t('tds_guidlines_text')}</Text>
44
- )}
40
+ </ScrollView>
45
41
  </SafeAreaView>
46
42
  );
47
43
  });
@@ -28,8 +28,13 @@ const MoreMenu = memo(
28
28
  text: t('members'),
29
29
  data: { unitId: unit.id, unit },
30
30
  };
31
+ const ListSmartAccount = {
32
+ route: Routes.ListSmartAccount,
33
+ text: t('smart_account'),
34
+ data: { unitId: unit.id, unit },
35
+ };
31
36
  return isOwner
32
- ? [RouteManageUnit, RouteUnitMemberList]
37
+ ? [RouteManageUnit, RouteUnitMemberList, ListSmartAccount]
33
38
  : [RouteUnitMemberList];
34
39
  }, [t, unit, isOwner]);
35
40
 
@@ -8,8 +8,8 @@ import BottomButtonView from '../../commons/BottomButtonView';
8
8
  import { useTranslations } from '../../hooks/Common/useTranslations';
9
9
  import { API } from '../../configs';
10
10
  import styles from './SelectLocationStyles';
11
- import SearchBarLocation from './components/SearchLocation';
12
- import RowLocation from './components/SearchLocation/RowLocation';
11
+ import SearchBarLocation from '../../commons/SearchLocation';
12
+ import RowLocation from '../../commons/SearchLocation/RowLocation';
13
13
  import { axiosGet } from '../../utils/Apis/axios';
14
14
  import { ScrollView } from 'react-native';
15
15
  import { useNavigation } from '@react-navigation/native';
@@ -109,7 +109,6 @@ const SelectLocation = memo(({ route }) => {
109
109
  updateLocation(searchedLocation.description);
110
110
  goBack();
111
111
  }, [goBack, updateLocation, searchedLocation]);
112
-
113
112
  return (
114
113
  <View style={styles.wrap}>
115
114
  <View style={styles.searchLocation}>
@@ -0,0 +1,141 @@
1
+ import WrapHeaderScrollable from '../../commons/Sharing/WrapHeaderScrollable';
2
+ import React, {
3
+ useCallback,
4
+ useEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ memo,
9
+ } from 'react';
10
+ import { View } from 'react-native';
11
+ import styles from './SmartAccountStyles';
12
+ import { API, Colors } from '../../configs';
13
+ import Routes from '../../utils/Route';
14
+ import { useNavigation } from '@react-navigation/native';
15
+ import { axiosDelete, axiosGet } from '../../utils/Apis/axios';
16
+ import { SmartAccountItem } from './SmartAccountItem';
17
+ import { usePopover, useBoolean } from '../../hooks/Common';
18
+ import { AlertAction, MenuActionMore } from '../../commons';
19
+ import { useTranslations } from '../../hooks/Common/useTranslations';
20
+ import { useStateAlertRemove } from '../Unit/hook/useStateAlertRemove';
21
+
22
+ const ListSmartAccount = ({ route }) => {
23
+ const { unitId } = route?.params || {};
24
+ const t = useTranslations();
25
+ const [data, setData] = useState([]);
26
+ const smartAccountRef = useRef(null);
27
+ const { navigate } = useNavigation();
28
+
29
+ const getAllSmartAccounts = useCallback(async () => {
30
+ const { success, data } = await axiosGet(
31
+ API.SMART_ACCOUNT.LIST_SMART_ACCOUNT()
32
+ );
33
+ if (success) {
34
+ setData(data);
35
+ }
36
+ }, []);
37
+
38
+ const { childRef, showingPopover, showPopoverWithRef, hidePopover } =
39
+ usePopover();
40
+
41
+ const [lockShowing, acquireLockShowing, releaseLockShowing] = useBoolean();
42
+ const { stateAlertRemove, onShowRemoveAlert, hideAlertAction } =
43
+ useStateAlertRemove();
44
+
45
+ useEffect(() => {
46
+ getAllSmartAccounts();
47
+ // eslint-disable-next-line react-hooks/exhaustive-deps
48
+ }, []);
49
+
50
+ const onShowMenuMore = useCallback(
51
+ (buttonMenuRef, smartAccountItem) => {
52
+ smartAccountRef.current = smartAccountItem;
53
+ showPopoverWithRef(buttonMenuRef);
54
+ },
55
+ [showPopoverWithRef]
56
+ );
57
+
58
+ const onItemClick = useCallback(
59
+ (item) => {
60
+ if (!smartAccountRef?.current) {
61
+ return;
62
+ }
63
+ if (item.action === 'delete') {
64
+ acquireLockShowing();
65
+ onShowRemoveAlert(smartAccountRef.current.brand)();
66
+ }
67
+ },
68
+ [acquireLockShowing, onShowRemoveAlert]
69
+ );
70
+
71
+ const deleteSmartAccount = useCallback(async () => {
72
+ hideAlertAction();
73
+ if (!smartAccountRef?.current) {
74
+ return;
75
+ }
76
+ const id = smartAccountRef.current.id;
77
+ const { success } = await axiosDelete(
78
+ API.SMART_ACCOUNT.REMOVE_SMART_ACCOUNT(id)
79
+ );
80
+ success && getAllSmartAccounts();
81
+ }, [hideAlertAction, getAllSmartAccounts]);
82
+
83
+ const listMenuItem = useMemo(() => {
84
+ return [{ action: 'delete', text: t('delete') }];
85
+ }, [t]);
86
+
87
+ const gotoSmartAccountDetail = useCallback(
88
+ (item) => {
89
+ navigate(Routes.ListDeviceSmartAccount, {
90
+ username: item.username,
91
+ brand: item.brand,
92
+ smart_account_id: item.id,
93
+ unit_id: unitId,
94
+ });
95
+ },
96
+ [navigate, unitId]
97
+ );
98
+
99
+ return (
100
+ <View style={styles.wrap}>
101
+ <WrapHeaderScrollable
102
+ style={{ backgroundColor: Colors.White }}
103
+ title={t('smart_account')}
104
+ onRefresh={getAllSmartAccounts}
105
+ headerAniStyle={styles.headerAniStyle}
106
+ >
107
+ <View style={styles.wrapContent}>
108
+ {data.map((item) => {
109
+ return (
110
+ <SmartAccountItem
111
+ gotoSmartAccountDetail={gotoSmartAccountDetail}
112
+ item={item}
113
+ onShowMenuMore={onShowMenuMore}
114
+ />
115
+ );
116
+ })}
117
+ </View>
118
+ <AlertAction
119
+ visible={stateAlertRemove.visible && lockShowing}
120
+ hideModal={hideAlertAction}
121
+ title={stateAlertRemove.title}
122
+ message={stateAlertRemove.message}
123
+ leftButtonTitle={stateAlertRemove.leftButton}
124
+ leftButtonClick={hideAlertAction}
125
+ rightButtonTitle={stateAlertRemove.rightButton}
126
+ rightButtonClick={deleteSmartAccount}
127
+ />
128
+ <MenuActionMore
129
+ isVisible={showingPopover}
130
+ hideMore={hidePopover}
131
+ listMenuItem={listMenuItem}
132
+ childRef={childRef}
133
+ onItemClick={onItemClick}
134
+ onHide={releaseLockShowing}
135
+ />
136
+ </WrapHeaderScrollable>
137
+ </View>
138
+ );
139
+ };
140
+
141
+ export default memo(ListSmartAccount);
@@ -0,0 +1,51 @@
1
+ import FImage from '../../commons/FImage';
2
+ import { TouchableOpacity, View } from 'react-native';
3
+ import React, { memo, useRef } from 'react';
4
+ import styles from './SmartAccountStyles';
5
+ import { Colors } from '../../configs';
6
+ import Text from '../../commons/Text';
7
+ import { Icon } from '@ant-design/react-native';
8
+ import { TESTID } from '../../configs/Constants';
9
+
10
+ export const SmartAccountItem = memo(
11
+ ({ item, onShowMenuMore, gotoSmartAccountDetail }) => {
12
+ const onPressMore = () => {
13
+ onShowMenuMore(buttonMoreRef, item);
14
+ };
15
+
16
+ const onPressGotoSmartAccountDetail = () => {
17
+ gotoSmartAccountDetail(item);
18
+ };
19
+ const buttonMoreRef = useRef(null);
20
+
21
+ return (
22
+ <View style={styles.wrapItem}>
23
+ <TouchableOpacity
24
+ style={styles.wrapText}
25
+ onPress={onPressGotoSmartAccountDetail}
26
+ >
27
+ <FImage
28
+ source={{ uri: item?.logo }}
29
+ style={styles.icon}
30
+ resizeMode={'cover'}
31
+ />
32
+ <View style={styles.wrap}>
33
+ <Text numberOfLines={1} type="H4">
34
+ {item?.brand}
35
+ </Text>
36
+ <Text numberOfLines={2} type="Body" color={Colors.Gray7}>
37
+ {item?.username}
38
+ </Text>
39
+ </View>
40
+ </TouchableOpacity>
41
+ <TouchableOpacity
42
+ testID={TESTID.ITEM_QUICK_ACTION_PRESS}
43
+ onPress={onPressMore}
44
+ ref={buttonMoreRef}
45
+ >
46
+ <Icon name={'more'} size={27} color={Colors.Black} />
47
+ </TouchableOpacity>
48
+ </View>
49
+ );
50
+ }
51
+ );
@@ -0,0 +1,46 @@
1
+ import { Platform, StyleSheet } from 'react-native';
2
+ import { getBottomSpace } from 'react-native-iphone-x-helper';
3
+ import { Colors } from '../../configs';
4
+
5
+ export default StyleSheet.create({
6
+ wrap: {
7
+ flex: 1,
8
+ backgroundColor: Colors.White,
9
+ },
10
+ headerAniStyle: {
11
+ borderBottomWidth: 0,
12
+ },
13
+ wrapContent: {
14
+ flex: 1,
15
+ paddingRight: 48,
16
+ paddingTop: 32,
17
+ paddingBottom: Platform.select({
18
+ ios: getBottomSpace() + 10,
19
+ android: 120,
20
+ }),
21
+ },
22
+ wrapItem: {
23
+ marginBottom: 32,
24
+ flexDirection: 'row',
25
+ alignItems: 'center',
26
+ },
27
+ wrapText: {
28
+ flexDirection: 'row',
29
+ alignItems: 'space-arou',
30
+ },
31
+ icon: {
32
+ width: 48,
33
+ height: 48,
34
+ borderRadius: 2,
35
+ marginHorizontal: 16,
36
+ borderColor: Colors.Gray5,
37
+ borderWidth: 1,
38
+ },
39
+ bottomButtonView: {
40
+ paddingTop: 24,
41
+ paddingBottom: 32,
42
+ backgroundColor: Colors.White,
43
+ borderColor: Colors.ShadownTransparent,
44
+ borderTopWidth: 1,
45
+ },
46
+ });