@eohjsc/react-native-smart-city 0.7.8 → 0.7.9

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 (85) hide show
  1. package/assets/images/AddNewDevice/add-scan-device-icon.svg +13 -0
  2. package/assets/images/Email.svg +9 -0
  3. package/assets/images/lan.svg +3 -0
  4. package/assets/images/wifi-open.svg +3 -0
  5. package/package.json +2 -1
  6. package/src/commons/ActionGroup/StatesGridActionTemplate.js +7 -3
  7. package/src/commons/ActionGroup/__test__/StatesGridActionTemplate.test.js +7 -3
  8. package/src/commons/AlertAction/index.js +1 -0
  9. package/src/commons/Auth/AccountItem.js +17 -3
  10. package/src/commons/Auth/AccountList.js +3 -7
  11. package/src/commons/ConnectWifi/__test__/ConnectWifi.test.js +373 -0
  12. package/src/commons/ConnectWifi/index.js +201 -0
  13. package/src/commons/ConnectWifi/styles.js +69 -0
  14. package/src/commons/Form/TextInputPassword.js +1 -1
  15. package/src/commons/OneTapTemplate/StatesGridActionTemplate.js +6 -2
  16. package/src/configs/API.js +12 -0
  17. package/src/configs/AccessibilityLabel.js +7 -0
  18. package/src/configs/Constants.js +1 -0
  19. package/src/hooks/Common/index.js +2 -2
  20. package/src/hooks/Common/useBlockBack.js +36 -0
  21. package/src/hooks/useMqtt.js +10 -5
  22. package/src/navigations/AddGatewayStack.js +2 -0
  23. package/src/navigations/AllGatewayStack.js +4 -0
  24. package/src/navigations/Main.js +2 -2
  25. package/src/navigations/UnitStack.js +32 -0
  26. package/src/screens/AddNewGateway/ConnectingWifiDevice.js +7 -6
  27. package/src/screens/AddNewGateway/ScanDeviceLocal.js +267 -0
  28. package/src/screens/AddNewGateway/ScanDeviceLocalStyles.js +58 -0
  29. package/src/screens/AddNewGateway/SelectDeviceSubUnit.js +10 -2
  30. package/src/screens/AddNewGateway/SelectDeviceType.js +19 -2
  31. package/src/screens/AddNewGateway/__test__/ScanDeviceLocal.test.js +475 -0
  32. package/src/screens/AddNewGateway/__test__/SelectDeviceType.test.js +2 -2
  33. package/src/screens/AddNewGateway/configs/API.js +8 -0
  34. package/src/screens/AddNewGateway/hooks/useConnectDevice.js +59 -0
  35. package/src/screens/AllGateway/GatewayInfo/__test__/index.test.js +58 -1
  36. package/src/screens/AllGateway/GatewayInfo/index.js +8 -6
  37. package/src/screens/AllGateway/GatewayWifi/__test__/index.test.js +319 -0
  38. package/src/screens/AllGateway/GatewayWifi/index.js +107 -0
  39. package/src/screens/AllGateway/Successfully/__test__/index.test.js +77 -0
  40. package/src/screens/AllGateway/Successfully/index.js +66 -0
  41. package/src/screens/AllGateway/Successfully/styles.js +35 -0
  42. package/src/screens/AllGateway/components/Information/index.js +17 -1
  43. package/src/screens/AllGateway/components/RowItem/index.js +12 -1
  44. package/src/screens/AllGateway/hooks/__test__/index.test.js +18 -0
  45. package/src/screens/AllGateway/hooks/useGateway.js +13 -0
  46. package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +3 -3
  47. package/src/screens/Automate/AddNewAction/SetupScriptEmail.js +79 -0
  48. package/src/screens/Automate/AddNewAction/SetupScriptReceiverEmail.js +166 -0
  49. package/src/screens/Automate/AddNewAction/Styles/SetupScriptEmailStyles.js +37 -0
  50. package/src/screens/Automate/AddNewAction/Styles/SetupScriptReceiverEmailStyles.js +79 -0
  51. package/src/screens/Automate/AddNewAction/__test__/ChooseAction.test.js +1 -1
  52. package/src/screens/Automate/AddNewAction/__test__/SetupConfigCondition.test.js +13 -5
  53. package/src/screens/Automate/AddNewAction/__test__/SetupScriptEmail.test.js +76 -0
  54. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverEmail.test.js +105 -0
  55. package/src/screens/Automate/EditActionsList/Styles/UpdateReceiverEmailScriptStyles.js +78 -0
  56. package/src/screens/Automate/EditActionsList/UpdateEmailScript.js +80 -0
  57. package/src/screens/Automate/EditActionsList/UpdateReceiverEmailScript.js +179 -0
  58. package/src/screens/Automate/EditActionsList/__tests__/UpdateEmailScript.test.js +81 -0
  59. package/src/screens/Automate/EditActionsList/__tests__/UpdateReceiverEmailScript.test.js +83 -0
  60. package/src/screens/Automate/EditActionsList/__tests__/index.test.js +38 -5
  61. package/src/screens/Automate/EditActionsList/index.js +59 -2
  62. package/src/screens/Automate/ScriptDetail/Components/AddActionScript.js +20 -0
  63. package/src/screens/Automate/ScriptDetail/Styles/indexStyles.js +5 -3
  64. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +127 -21
  65. package/src/screens/Automate/ScriptDetail/index.js +57 -14
  66. package/src/screens/SharedUnit/index.js +2 -2
  67. package/src/screens/Sharing/SelectUser.js +47 -47
  68. package/src/screens/Sharing/__test__/SelectUser.test.js +57 -103
  69. package/src/screens/SubUnit/ManageSubUnit.js +94 -90
  70. package/src/screens/SubUnit/ManageSubUnitStyles.js +4 -6
  71. package/src/screens/SubUnit/RearrageSubUnit.js +90 -0
  72. package/src/screens/SubUnit/RearrrageSubUnitStyle.js +65 -0
  73. package/src/screens/SubUnit/__test__/ManageSubUnit.test.js +35 -19
  74. package/src/screens/SubUnit/__test__/RearrangeSubUnit.test.js +129 -0
  75. package/src/screens/SubUnit/hooks/__test__/useManageSubUnit.test.js +6 -7
  76. package/src/screens/SubUnit/hooks/useManageSubUnit.js +8 -16
  77. package/src/screens/Unit/Detail.js +2 -6
  78. package/src/screens/Unit/ManageUnit.js +1 -1
  79. package/src/utils/Functions/__test__/ShortEmail.test.js +5 -0
  80. package/src/utils/I18n/translations/en.js +46 -8
  81. package/src/utils/I18n/translations/vi.js +37 -4
  82. package/src/utils/Route/index.js +7 -0
  83. package/src/commons/Auth/__test__/AccountItem.test.js +0 -31
  84. package/src/hooks/Common/useBlockBackAndroid.js +0 -21
  85. package/src/screens/SubUnit/DetailStyles.js +0 -46
@@ -2,7 +2,6 @@ import React from 'react';
2
2
  import { create, act } from 'react-test-renderer';
3
3
  import MockAdapter from 'axios-mock-adapter';
4
4
 
5
- import { AccessibilityLabel } from '../../../configs/Constants';
6
5
  import { ViewButtonBottom, Button } from '../../../commons';
7
6
  import _TextInput from '../../../commons/Form/TextInput';
8
7
  import AccountList from '../../../commons/Auth/AccountList';
@@ -11,8 +10,9 @@ import { getTranslate } from '../../../utils/I18n';
11
10
  import { SCProvider } from '../../../context';
12
11
  import { mockSCStore } from '../../../context/mockStore';
13
12
  import api from '../../../utils/Apis/axios';
14
- import { Text } from 'react-native';
15
13
  import SharingInviteMembers from '../SelectUser';
14
+ import { ToastBottomHelper } from '../../../utils/Utils';
15
+ import { IconOutline } from '@ant-design/icons-react-native';
16
16
 
17
17
  const wrapComponent = (route) => (
18
18
  <SCProvider initState={mockSCStore({})}>
@@ -40,62 +40,7 @@ describe('test SharingInviteMembers container', () => {
40
40
  };
41
41
  });
42
42
 
43
- const findByTestId = (instance, id) => {
44
- return instance.find((el) => el.props.accessibilityLabel === id);
45
- };
46
-
47
- it('create', async () => {
48
- await act(async () => {
49
- tree = await create(wrapComponent(route));
50
- });
51
- const instance = tree.root;
52
- const textTitle = findByTestId(
53
- instance,
54
- AccessibilityLabel.SELECT_USER_ADD_USER_TITLE
55
- );
56
- const textSubTitle = findByTestId(
57
- instance,
58
- AccessibilityLabel.SELECT_USER_ADD_USER_SUB_TITLE
59
- );
60
- expect(textTitle.props.children).toEqual(
61
- getTranslate('en', 'add_user_title')
62
- );
63
- expect(textSubTitle.props.children).toEqual(
64
- getTranslate('en', 'add_user_sub_title')
65
- );
66
- });
67
-
68
- it('viewButtonBottom and onLeftClick', async () => {
69
- await act(async () => {
70
- tree = await create(wrapComponent(route));
71
- });
72
- const instance = tree.root;
73
- const viewButtonBottom = instance.findByType(ViewButtonBottom);
74
- expect(viewButtonBottom.props.leftTitle).toEqual(
75
- getTranslate('en', 'back')
76
- );
77
- expect(viewButtonBottom.props.rightTitle).toEqual(
78
- getTranslate('en', 'done')
79
- );
80
- await act(async () => {
81
- viewButtonBottom.props.onLeftClick();
82
- });
83
- expect(global.mockedGoBack).toHaveBeenCalled();
84
- });
85
-
86
- it('viewButtonBottom onRightClick', async () => {
87
- await act(async () => {
88
- tree = await create(wrapComponent(route));
89
- });
90
- const instance = tree.root;
91
- const viewButtonBottom = instance.findByType(ViewButtonBottom);
92
- await act(async () => {
93
- viewButtonBottom.props.onRightClick();
94
- });
95
- expect(global.mockedNavigate).toHaveBeenCalled();
96
- });
97
-
98
- it('_TextInput onChange phone, invalidate and not call api sharedPermission', async () => {
43
+ it('input phone invalid', async () => {
99
44
  mock.onPost(API.SHARE.SHARE()).reply(200);
100
45
  await act(async () => {
101
46
  tree = await create(wrapComponent(route));
@@ -118,83 +63,92 @@ describe('test SharingInviteMembers container', () => {
118
63
  expect(textInput.props.errorText).toEqual(
119
64
  getTranslate('en', 'invalid_phone_number_or_email')
120
65
  );
66
+ const viewButtonBottom = instance.findByType(ViewButtonBottom);
67
+ expect(viewButtonBottom.props.rightDisabled).toBeTruthy();
121
68
  });
122
69
 
123
- it('_TextInput onChange phone, validated and call api sharedPermission', async () => {
70
+ it('input phone valid and call api share permission', async () => {
71
+ const phone = '0909123456';
72
+ const spyToast = jest.spyOn(ToastBottomHelper, 'success');
73
+ mock.onGet(API.SHARE.SEARCH_USER_BY_PHONE(phone)).reply(200, {
74
+ id: 3,
75
+ name: 'user add',
76
+ phone_number: phone,
77
+ });
78
+
79
+ mock.onPost(API.SHARE.SHARE_V2()).reply(200);
124
80
  await act(async () => {
125
81
  tree = await create(wrapComponent(route));
126
82
  });
127
83
  const instance = tree.root;
128
84
  const textInput = instance.findByType(_TextInput);
129
- const button = instance.findByType(Button);
85
+ const buttonCheckUser = instance.findByType(Button);
130
86
  let accountList = instance.findAllByType(AccountList);
131
87
  expect(accountList).toHaveLength(0);
132
88
  await act(async () => {
133
- await textInput.props.onChange('0909123456');
134
- });
135
- mock.onPost(API.SHARE.SHARE()).reply(200, {
136
- user: {
137
- id: 3,
138
- name: 'user add',
139
- phone_number: '0909123456',
140
- },
89
+ await textInput.props.onChange(phone);
141
90
  });
142
91
  await act(async () => {
143
- await button.props.onPress();
92
+ await buttonCheckUser.props.onPress();
93
+ await buttonCheckUser.props.onPress(); // case user double click
144
94
  });
145
- expect(textInput.props.errorText).toEqual('');
95
+
146
96
  accountList = instance.findAllByType(AccountList);
147
97
  expect(accountList).toHaveLength(1);
148
98
  expect(accountList[0].props.accounts).toHaveLength(1);
149
99
 
100
+ const viewButtonBottom = instance.findByType(ViewButtonBottom);
150
101
  await act(async () => {
151
- await textInput.props.onChange('test123@gmail.com');
152
- });
153
- mock.onPost(API.SHARE.SHARE()).reply(400);
154
- await act(async () => {
155
- await button.props.onPress();
102
+ viewButtonBottom.props.onRightClick();
156
103
  });
157
- expect(accountList[0].props.accounts).toHaveLength(1);
104
+ expect(spyToast).toBeCalledWith('Invited user');
105
+ });
158
106
 
159
- await act(async () => {
160
- await textInput.props.onChange('0909123456'); // In case you have invited and then intentionally invite again
107
+ it('input 2 email valid and remove 1 email then call api share permission', async () => {
108
+ const email_1 = 'test1@gmail.com';
109
+ const email_2 = 'test2@gmail.com';
110
+ const spyToast = jest.spyOn(ToastBottomHelper, 'success');
111
+ mock.onGet(API.SHARE.SEARCH_USER_BY_EMAIL(email_1)).reply(200, {
112
+ id: 1,
113
+ email: email_1,
161
114
  });
162
- mock.onPost(API.SHARE.SHARE()).reply(400);
163
- await act(async () => {
164
- await button.props.onPress();
115
+ mock.onGet(API.SHARE.SEARCH_USER_BY_EMAIL(email_2)).reply(200, {
116
+ id: 2,
117
+ email: email_2,
165
118
  });
166
- expect(accountList[0].props.accounts).toHaveLength(1);
167
- });
168
-
169
- it('_TextInput onChange email, validated and call api sharedPermission', async () => {
119
+ mock.onPost(API.SHARE.SHARE_V2()).reply(200);
170
120
  await act(async () => {
171
121
  tree = await create(wrapComponent(route));
172
122
  });
173
123
  const instance = tree.root;
174
124
  const textInput = instance.findByType(_TextInput);
175
- const button = instance.findByType(Button);
176
- let accountList = instance.findAllByType(AccountList);
177
- expect(accountList).toHaveLength(0);
125
+ const buttonCheckUser = instance.findByType(Button);
126
+ expect(instance.findAllByType(AccountList)).toHaveLength(0);
178
127
  await act(async () => {
179
- await textInput.props.onChange('test@eoh.io');
128
+ await textInput.props.onChange(email_1);
129
+ await buttonCheckUser.props.onPress();
180
130
  });
181
- mock.onPost(API.SHARE.SHARE()).reply(200, {
182
- user: {
183
- id: 3,
184
- name: 'user add',
185
- phone_number: '',
186
- email: 'test@eoh.io',
187
- },
131
+
132
+ expect(instance.findByType(AccountList).props.accounts).toHaveLength(1);
133
+
134
+ await act(async () => {
135
+ await textInput.props.onChange(email_2);
136
+ await buttonCheckUser.props.onPress();
188
137
  });
138
+
139
+ expect(instance.findByType(AccountList).props.accounts).toHaveLength(2);
140
+
141
+ const iconRemoves = instance.findAllByType(IconOutline);
189
142
  await act(async () => {
190
- await button.props.onPress();
143
+ await iconRemoves[0].props.onPress();
191
144
  });
192
145
 
193
- const textPhone = instance.findAll(
194
- (el) =>
195
- el.props.accessibilityLabel ===
196
- AccessibilityLabel.TEXT_PHONE_NUMBER_ITEM && el.type === Text
197
- );
198
- expect(textPhone).toHaveLength(0);
146
+ expect(instance.findByType(AccountList).props.accounts).toHaveLength(1);
147
+
148
+ const viewButtonBottom = instance.findByType(ViewButtonBottom);
149
+ await act(async () => {
150
+ viewButtonBottom.props.onRightClick();
151
+ });
152
+ expect(spyToast).toBeCalledWith('Invited user');
199
153
  });
200
154
  });
@@ -1,42 +1,93 @@
1
- import React, { useMemo, useCallback, useEffect } from 'react';
2
- import {
3
- View,
4
- TouchableOpacity,
5
- Image,
6
- ScrollView,
7
- RefreshControl,
8
- } from 'react-native';
1
+ import React, { useEffect } from 'react';
2
+ import { View, TouchableOpacity, Image } from 'react-native';
9
3
  import { useIsFocused, useNavigation } from '@react-navigation/native';
10
4
  import { Colors } from '../../configs';
11
5
  import { useTranslations } from '../../hooks/Common/useTranslations';
12
-
13
6
  import WrapHeaderScrollable from '../../commons/Sharing/WrapHeaderScrollable';
14
7
  import Text from '../../commons/Text';
15
-
16
8
  import styles from './ManageSubUnitStyles';
17
9
  import { IconOutline } from '@ant-design/icons-react-native';
18
10
  import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
19
11
  import Routes from '../../utils/Route';
20
- import { RowItem } from '../../commons/RowItem';
21
12
  import NoSubUnitImage from '../../../assets/images/Illustrations.svg';
22
13
  import useManageSubUnit from './hooks/useManageSubUnit';
23
14
  import { AccessibilityLabel } from '../../configs/Constants';
15
+ import { RowItem } from '../../commons/RowItem';
16
+
17
+ const RightComponent = ({ onAddSubUnit, onRearrangeSubUnit }) => (
18
+ <View style={styles.rightComponent}>
19
+ <TouchableOpacity
20
+ accessibilityLabel={AccessibilityLabel.ADD_SUB_UNIT}
21
+ onPress={onAddSubUnit}
22
+ style={styles.headerButton}
23
+ >
24
+ <IconOutline name={'plus'} size={27} color={Colors.Black} />
25
+ </TouchableOpacity>
26
+ <TouchableOpacity
27
+ accessibilityLabel={AccessibilityLabel.REARRANGE_SUB_UNIT}
28
+ onPress={onRearrangeSubUnit}
29
+ style={[styles.headerButton, styles.moreButton]}
30
+ >
31
+ <MaterialIcons name={'reorder'} size={27} color={Colors.Black} />
32
+ </TouchableOpacity>
33
+ </View>
34
+ );
35
+
36
+ const StationList = ({ stations, onEditSubUnit }) => {
37
+ const t = useTranslations();
38
+ return (
39
+ <>
40
+ {stations.map((item, index) => (
41
+ <RowItem
42
+ type={'noneBG'}
43
+ key={index.toString()}
44
+ index={index}
45
+ leftIcon={
46
+ <Image
47
+ source={{ uri: item.background }}
48
+ style={styles.image}
49
+ resizeMode="cover"
50
+ />
51
+ }
52
+ text={item.name}
53
+ subtext={`${item.devices.length} ${t('devices').toLowerCase()}`}
54
+ onPress={() => onEditSubUnit(item)}
55
+ rightComponent={
56
+ <IconOutline name="right" size={20} color={Colors.Gray6} />
57
+ }
58
+ />
59
+ ))}
60
+ </>
61
+ );
62
+ };
63
+
64
+ const NoSubUnit = ({ noSubUnitText, addSubUnitText }) => (
65
+ <View style={styles.noSubUnit}>
66
+ <NoSubUnitImage />
67
+ <Text semibold type="H4" center>
68
+ {noSubUnitText}
69
+ </Text>
70
+ <Text type="Body" center>
71
+ {addSubUnitText}
72
+ </Text>
73
+ </View>
74
+ );
24
75
 
25
76
  const ManageSubUnit = ({ route }) => {
26
77
  const t = useTranslations();
27
78
  const { unit } = route.params;
28
79
  const navigation = useNavigation();
29
80
  const isFocused = useIsFocused();
30
- const { stations, isRefresh, isLoading, onRefresh } = useManageSubUnit(unit);
81
+ const { stations, refreshing, onRefresh } = useManageSubUnit(unit.id);
31
82
 
32
- const addSubUnit = useCallback(() => {
83
+ const handleAddSubUnit = () => {
33
84
  navigation.navigate(Routes.AddSubUnitStack, {
34
85
  screen: Routes.AddSubUnit,
35
86
  params: { unit },
36
87
  });
37
- }, [navigation, unit]);
88
+ };
38
89
 
39
- const goToEditSubUnit = (station) => {
90
+ const handleEditSubUnit = (station) => {
40
91
  navigation.navigate(Routes.UnitStack, {
41
92
  screen: Routes.EditSubUnit,
42
93
  params: {
@@ -46,29 +97,15 @@ const ManageSubUnit = ({ route }) => {
46
97
  });
47
98
  };
48
99
 
49
- const rightComponent = useMemo(
50
- () => (
51
- <View style={styles.rightComponent}>
52
- <TouchableOpacity
53
- accessibilityLabel={AccessibilityLabel.ADD_SUB_UNIT}
54
- onPress={addSubUnit}
55
- style={styles.headerButton}
56
- >
57
- <IconOutline name={'plus'} size={27} color={Colors.Black} />
58
- </TouchableOpacity>
59
- <TouchableOpacity
60
- // onPress={handleShowMenuAction}
61
- // ref={refMenuAction}
62
- style={[styles.headerButton, styles.moreButton]}
63
- >
64
- <MaterialIcons name={'more-vert'} size={27} color={Colors.Black} />
65
- </TouchableOpacity>
66
- </View>
67
- ),
68
- // eslint-disable-next-line react-hooks/exhaustive-deps
69
- []
70
- );
71
-
100
+ const handleRearrageSubUnit = () => {
101
+ navigation.navigate(Routes.UnitStack, {
102
+ screen: Routes.RearrangeSubUnit,
103
+ params: {
104
+ stations,
105
+ unit,
106
+ },
107
+ });
108
+ };
72
109
  useEffect(() => {
73
110
  isFocused && onRefresh();
74
111
  }, [onRefresh, isFocused]);
@@ -80,60 +117,27 @@ const ManageSubUnit = ({ route }) => {
80
117
  >
81
118
  <WrapHeaderScrollable
82
119
  title={t('manage_sub_unit')}
83
- headerAniStyle={styles.headerAniStyle}
84
- headerAniCenterStyle={true}
85
- rightComponent={rightComponent}
120
+ rightComponent={
121
+ <RightComponent
122
+ onAddSubUnit={handleAddSubUnit}
123
+ onRearrangeSubUnit={handleRearrageSubUnit}
124
+ />
125
+ }
126
+ onRefresh={onRefresh}
127
+ loading={refreshing}
86
128
  >
87
129
  <View style={styles.container}>
88
- <ScrollView
89
- refreshControl={
90
- <RefreshControl refreshing={false} onRefresh={onRefresh} />
91
- }
92
- scrollIndicatorInsets={{ right: 1 }}
93
- >
94
- <View>
95
- {!!stations.length &&
96
- stations.map((item, index) => {
97
- return (
98
- <RowItem
99
- type={'noneBG'}
100
- key={index.toString()}
101
- index={index}
102
- leftIcon={
103
- <Image
104
- source={{ uri: item.background }}
105
- style={styles.image}
106
- resizeMode="cover"
107
- />
108
- }
109
- text={item.name}
110
- subtext={`${
111
- item.devices ? item.devices.length : '0'
112
- } devices`}
113
- onPress={() => goToEditSubUnit(item, unit)}
114
- rightComponent={
115
- <IconOutline
116
- name="right"
117
- size={20}
118
- color={Colors.Gray6}
119
- />
120
- }
121
- />
122
- );
123
- })}
124
- {!stations.length && !isRefresh && !isLoading && (
125
- <View style={styles.NoSubUnit}>
126
- <NoSubUnitImage />
127
- <Text semibold type="H4" center>
128
- {t('no_sub_unit_yet')}
129
- </Text>
130
- <Text type="Body" center>
131
- {t('tap_+_to_add_new_sub_unit')}
132
- </Text>
133
- </View>
134
- )}
135
- </View>
136
- </ScrollView>
130
+ {!!stations.length ? (
131
+ <StationList
132
+ stations={stations}
133
+ onEditSubUnit={handleEditSubUnit}
134
+ />
135
+ ) : (
136
+ <NoSubUnit
137
+ noSubUnitText={t('no_sub_unit_yet')}
138
+ addSubUnitText={t('tap_+_to_add_new_sub_unit')}
139
+ />
140
+ )}
137
141
  </View>
138
142
  </WrapHeaderScrollable>
139
143
  </View>
@@ -3,16 +3,12 @@ import { Colors } from '../../configs';
3
3
  import { Constants } from '../../configs';
4
4
 
5
5
  export default StyleSheet.create({
6
- headerAniStyle: {
7
- paddingBottom: 16,
8
- borderBottomWidth: 0,
9
- },
10
6
  rightComponent: {
11
7
  flexDirection: 'row',
12
8
  alignItems: 'flex-end',
13
9
  },
14
10
  headerButton: {
15
- marginRight: 20,
11
+ marginRight: 16,
16
12
  },
17
13
  moreButton: {
18
14
  marginLeft: -5,
@@ -23,11 +19,12 @@ export default StyleSheet.create({
23
19
  },
24
20
  container: {
25
21
  paddingTop: 0,
22
+ flex: 1,
26
23
  },
27
24
  paddingLeft16: {
28
25
  paddingLeft: 16,
29
26
  },
30
- NoSubUnit: {
27
+ noSubUnit: {
31
28
  alignSelf: 'center',
32
29
  marginTop: Constants.height * 0.2,
33
30
  fontSize: 16,
@@ -36,5 +33,6 @@ export default StyleSheet.create({
36
33
  width: 40,
37
34
  height: 40,
38
35
  borderRadius: 40 / 2,
36
+ backgroundColor: Colors.White,
39
37
  },
40
38
  });
@@ -0,0 +1,90 @@
1
+ import React, { useState } from 'react';
2
+ import { View, TouchableOpacity } from 'react-native';
3
+ import { isEqual } from 'lodash';
4
+ import { API } from '../../configs';
5
+ import { axiosPost } from '../../utils/Apis/axios';
6
+ import styles from './RearrrageSubUnitStyle';
7
+ import Text from '../../commons/Text';
8
+ import FlatListDnD from '../../commons/FlatListDnD';
9
+ import ViewButtonBottom from '../../commons/ViewButtonBottom';
10
+ import { HeaderCustom } from '../../commons/Header';
11
+ import Rearrange from '../../../assets/images/Rearrange.svg';
12
+ import { ToastBottomHelper } from '../../utils/Utils';
13
+ import { useNavigation } from '@react-navigation/native';
14
+ import { useTranslations } from '../../hooks/Common/useTranslations';
15
+
16
+ const RearrangeSubUnit = ({ route }) => {
17
+ const { stations, unit } = route.params;
18
+ const t = useTranslations();
19
+ const { goBack } = useNavigation();
20
+ const [reorderItems, setReorderItems] = useState(stations);
21
+ const handleOnSave = async () => {
22
+ const idOrderSubUnits = reorderItems.map((i) => i.id);
23
+ const { success } = await axiosPost(API.SUB_UNIT.ORDER_SUB_UNITS(unit.id), {
24
+ id_order_sub_units: idOrderSubUnits,
25
+ });
26
+ if (success) {
27
+ ToastBottomHelper.success(t('updated_sub_unit_order'));
28
+ goBack();
29
+ }
30
+ };
31
+
32
+ const renderItem = ({ item, getIndex, drag, isActive }) => {
33
+ const index = getIndex() + 1;
34
+ return (
35
+ <View style={styles.itemWrapper}>
36
+ <TouchableOpacity
37
+ style={[
38
+ styles.border,
39
+ styles.indexWrapper,
40
+ /* istanbul ignore next */
41
+ isActive && styles.isDragging,
42
+ ]}
43
+ onPressIn={drag}
44
+ >
45
+ <Text style={styles.indexText}>{`${index}`.padStart(2, '0')}</Text>
46
+ <Rearrange />
47
+ </TouchableOpacity>
48
+ <View
49
+ style={[
50
+ styles.border,
51
+ styles.subUnitWrapper,
52
+ /* istanbul ignore next */
53
+ isActive && styles.isDragging,
54
+ ]}
55
+ >
56
+ <Text style={styles.indexText}>{item.name}</Text>
57
+ <Text style={styles.deviceText}>
58
+ {`${item.devices.length} ${t('devices').toLowerCase()}`}
59
+ </Text>
60
+ </View>
61
+ </View>
62
+ );
63
+ };
64
+
65
+ return (
66
+ <View style={styles.container}>
67
+ <HeaderCustom title={t('rearrange_sub_unit')} isShowSeparator />
68
+ <Text style={styles.hintText}>{t('rearrange_sub_unit_hint')}</Text>
69
+ <View style={styles.flatList}>
70
+ <FlatListDnD
71
+ data={reorderItems}
72
+ extraData={reorderItems}
73
+ setData={setReorderItems}
74
+ renderItem={renderItem}
75
+ />
76
+ </View>
77
+ <ViewButtonBottom
78
+ leftTitle={t('cancel')}
79
+ onLeftClick={() => goBack()}
80
+ rightTitle={t('save')}
81
+ rightDisabled={isEqual(stations, reorderItems)}
82
+ onRightClick={handleOnSave}
83
+ isPreventDoubleTouch
84
+ wrapStyle={styles.bottomButtonWrapper}
85
+ />
86
+ </View>
87
+ );
88
+ };
89
+
90
+ export default RearrangeSubUnit;
@@ -0,0 +1,65 @@
1
+ import { Colors } from '../../configs';
2
+ import { StyleSheet } from 'react-native';
3
+
4
+ export default StyleSheet.create({
5
+ container: {
6
+ flex: 1,
7
+ backgroundColor: Colors.White,
8
+ },
9
+ border: {
10
+ borderRadius: 5,
11
+ borderColor: Colors.Gray6,
12
+ borderWidth: 1,
13
+ padding: 20,
14
+ backgroundColor: Colors.White,
15
+ shadowColor: Colors.Black,
16
+ shadowOffset: {
17
+ width: 0,
18
+ height: 10,
19
+ },
20
+ shadowOpacity: 0.23,
21
+ shadowRadius: 11.27,
22
+ elevation: 4,
23
+ },
24
+ flatList: {
25
+ flex: 0.95,
26
+ marginBottom: 10,
27
+ },
28
+ isDragging: {
29
+ borderBottomWidth: 1,
30
+ borderColor: Colors.Red,
31
+ },
32
+ itemWrapper: {
33
+ flexDirection: 'row',
34
+ marginBottom: 20,
35
+ marginHorizontal: 16,
36
+ backgroundColor: Colors.White,
37
+ },
38
+ indexWrapper: {
39
+ flexDirection: 'column',
40
+ justifyContent: 'center',
41
+ alignItems: 'center',
42
+ },
43
+ subUnitWrapper: {
44
+ flex: 1,
45
+ marginLeft: 10,
46
+ justifyContent: 'center',
47
+ },
48
+ hintText: {
49
+ marginHorizontal: 16,
50
+ marginTop: 10,
51
+ },
52
+ indexText: {
53
+ fontSize: 16,
54
+ lineHeight: 24,
55
+ fontWeight: 'bold',
56
+ },
57
+ deviceText: {
58
+ fontSize: 14,
59
+ lineHeight: 20,
60
+ color: Colors.Gray7,
61
+ },
62
+ bottomButtonWrapper: {
63
+ backgroundColor: Colors.White,
64
+ },
65
+ });