@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
@@ -0,0 +1,63 @@
1
+ import NetInfo from '@react-native-community/netinfo';
2
+ import { useCallback, useEffect } from 'react';
3
+ import { Alert, Linking, Platform } from 'react-native';
4
+ import { useTranslations } from '../../../hooks/Common/useTranslations';
5
+ import {
6
+ enableBluetoothForAndroid,
7
+ isBluetoothEnabled,
8
+ } from '../../../iot/RemoteControl/Bluetooth';
9
+
10
+ export const useDisconnectedDevice = (isConnected, sensorName) => {
11
+ const t = useTranslations();
12
+ const openBluetoothIOS = () => {
13
+ Linking.openURL('App-Prefs:Bluetooth');
14
+ };
15
+ const actions =
16
+ Platform.OS === 'ios'
17
+ ? [
18
+ {
19
+ text: 'Skip',
20
+ },
21
+ {
22
+ text: 'Open',
23
+ onPress: () => openBluetoothIOS(),
24
+ },
25
+ ]
26
+ : [
27
+ {
28
+ text: 'Skip',
29
+ },
30
+ {
31
+ text: 'Open',
32
+ onPress: () => enableBluetoothForAndroid(),
33
+ },
34
+ ];
35
+ const checkNetWorkConnect = useCallback(async () => {
36
+ const netState = await NetInfo.fetch();
37
+ if (!isConnected || !netState.isConnected) {
38
+ const isBtEnabled = await isBluetoothEnabled();
39
+ if (isBtEnabled) {
40
+ Alert.alert(
41
+ '',
42
+ t(
43
+ 'your_internet_is_disconnected_change_to_control_via_bluetooth_connection',
44
+ { name: sensorName }
45
+ )
46
+ );
47
+ } else {
48
+ Alert.alert(
49
+ '',
50
+ t(
51
+ 'your_connection_to_the_server_was_disconnected_please_open_the_bluetooth_to_continue'
52
+ ),
53
+ actions
54
+ );
55
+ }
56
+ }
57
+ // eslint-disable-next-line react-hooks/exhaustive-deps
58
+ }, [isConnected]);
59
+
60
+ useEffect(() => {
61
+ checkNetWorkConnect();
62
+ }, [checkNetWorkConnect]);
63
+ };
@@ -24,9 +24,6 @@ export default StyleSheet.create({
24
24
  paddingHorizontal: 16,
25
25
  paddingTop: 16,
26
26
  },
27
- chartStyle: {
28
- paddingHorizontal: 16,
29
- },
30
27
  bottomButtonEmergencyResolve: {
31
28
  borderTopWidth: 1,
32
29
  borderColor: Colors.Gray4,
@@ -37,13 +34,6 @@ export default StyleSheet.create({
37
34
  marginHorizontal: 16,
38
35
  marginBottom: 32,
39
36
  },
40
- countDown: {
41
- marginBottom: 16,
42
- marginTop: 8,
43
- },
44
- messageCountDown: {
45
- marginBottom: 8,
46
- },
47
37
  locationName: {
48
38
  flex: 1,
49
39
  alignItems: 'center',
@@ -73,7 +73,7 @@ const RecurringDetail = ({
73
73
  testID={TESTID.RECURRING_TEXT_BUTTON}
74
74
  >
75
75
  <Text type="Body" color={Colors.Orange} style={styles.value}>
76
- {recurringTimeStart.format('HH:mm A')}
76
+ {recurringTimeStart.format('hh:mm A')}
77
77
  </Text>
78
78
  </TouchableOpacity>
79
79
  <Text type="Body" color={Colors.Gray8} style={styles.title}>
@@ -84,7 +84,7 @@ const RecurringDetail = ({
84
84
  testID={TESTID.RECURRING_TEXT_BUTTON}
85
85
  >
86
86
  <Text type="Body" color={Colors.Orange} style={styles.value}>
87
- {recurringTimeEnd.format('HH:mm A')}
87
+ {recurringTimeEnd.format('hh:mm A')}
88
88
  </Text>
89
89
  </TouchableOpacity>
90
90
  <Text type="Body" color={Colors.Gray8} style={styles.title}>
@@ -1,11 +1,11 @@
1
1
  import React, { memo, useCallback, useMemo, useState } from 'react';
2
- import { View, TouchableOpacity } from 'react-native';
2
+ import { View, TouchableOpacity, Image } from 'react-native';
3
3
  import moment from 'moment';
4
4
  import { useNavigation } from '@react-navigation/native';
5
5
 
6
6
  import styles from '../styles/NotificationItemStyles';
7
7
  import Text from '../../../commons/Text';
8
- import { Colors, API } from '../../../configs';
8
+ import { Colors, API, Images } from '../../../configs';
9
9
  import IconComponent from '../../../commons/IconComponent';
10
10
  import { NOTIFICATION_TYPES } from '../../../configs/Constants';
11
11
  import { useTranslations } from '../../../hooks/Common/useTranslations';
@@ -18,14 +18,18 @@ const NotificationItem = memo(({ item }) => {
18
18
  const { id, icon, created_at, is_read, params, content_code } = item;
19
19
  const [isRead, setIsRead] = useState(is_read);
20
20
  const timeFormat = moment(created_at).format('LT DD/MM/YYYY');
21
+ const regex = useMemo(() => {
22
+ return /\B'|'\B/g;
23
+ }, []);
24
+
21
25
  let arrParams = useMemo(() => {
22
26
  const values = [];
23
- const paramsJSON = JSON.parse(params.replace(/'/g, '"'));
27
+ const paramsJSON = JSON.parse(params.replace(regex, '"'));
24
28
  Object.entries(paramsJSON).forEach(([key, value]) => {
25
29
  values.push(value);
26
30
  });
27
31
  return values;
28
- }, [params]);
32
+ }, [params, regex]);
29
33
 
30
34
  const customColorText = (text, params) => {
31
35
  return text.split('**').map((str, i) =>
@@ -42,7 +46,7 @@ const NotificationItem = memo(({ item }) => {
42
46
  };
43
47
 
44
48
  const renderItem = useCallback(() => {
45
- const paramsJSON = JSON.parse(params.replace(/'/g, '"'));
49
+ const paramsJSON = JSON.parse(params.replace(regex, '"'));
46
50
  const booking_id = paramsJSON.booking_id && paramsJSON.booking_id;
47
51
  switch (content_code) {
48
52
  case NOTIFICATION_TYPES.NOTIFY_INVITE_MEMBER:
@@ -60,7 +64,12 @@ const NotificationItem = memo(({ item }) => {
60
64
  },
61
65
  });
62
66
  },
63
- textInviteMember: t('accept_invitation'),
67
+ iconContent: (
68
+ <IconComponent
69
+ icon_outlined={'usergroup-add'}
70
+ style={styles.iconInviteMember}
71
+ />
72
+ ),
64
73
  };
65
74
  case NOTIFICATION_TYPES.REMIND_TO_MAKE_PAYMENT:
66
75
  return {
@@ -211,10 +220,19 @@ const NotificationItem = memo(({ item }) => {
211
220
  params: { id: booking_id },
212
221
  }),
213
222
  };
223
+ case NOTIFICATION_TYPES.REMINDER:
224
+ return {
225
+ content: customColorText(
226
+ t('text_notification_content_update_reminder'),
227
+ arrParams
228
+ ),
229
+ redirect: () => navigation.navigate(Routes.PersonalHealthStack),
230
+ iconContent: <Image source={Images.logo} style={styles.logo} />,
231
+ };
214
232
  }
215
- }, [arrParams, content_code, navigation, params, t]);
233
+ }, [arrParams, content_code, navigation, params, regex, t]);
216
234
 
217
- const { content, redirect, textInviteMember } = renderItem() || {};
235
+ const { content, redirect, iconContent } = renderItem() || {};
218
236
 
219
237
  const onItemPress = useCallback(() => {
220
238
  if (!isRead) {
@@ -224,16 +242,15 @@ const NotificationItem = memo(({ item }) => {
224
242
  setIsRead(true);
225
243
  }, [id, isRead, redirect]);
226
244
 
245
+ if (!content) {
246
+ return null;
247
+ }
248
+
227
249
  return (
228
250
  <TouchableOpacity onPress={onItemPress}>
229
251
  <View style={[styles.container, !isRead && styles.backgroundGray]}>
230
252
  <View style={styles.wrapIcon}>
231
- {textInviteMember ? (
232
- <IconComponent
233
- icon_outlined={'usergroup-add'}
234
- style={styles.iconInviteMember}
235
- />
236
- ) : (
253
+ {iconContent || (
237
254
  <IconComponent iconKit={icon} style={styles.iconNotification} />
238
255
  )}
239
256
  </View>
@@ -47,4 +47,9 @@ export default StyleSheet.create({
47
47
  borderRadius: 50,
48
48
  width: 120,
49
49
  },
50
+ logo: {
51
+ width: 20,
52
+ height: 20,
53
+ resizeMode: 'contain',
54
+ },
50
55
  });
@@ -1,10 +1,19 @@
1
1
  import React from 'react';
2
2
  import { create, act } from 'react-test-renderer';
3
3
  import axios from 'axios';
4
+
5
+ import { SCProvider } from '../../../context';
6
+ import { mockSCStore } from '../../../context/mockStore';
4
7
  import ScanChipQR from '..';
5
8
  import QRScan from '../components/QRScan';
6
9
  import Routes from '../../../utils/Route';
7
10
 
11
+ const wrapComponent = (route) => (
12
+ <SCProvider initState={mockSCStore({})}>
13
+ <ScanChipQR route={route} />
14
+ </SCProvider>
15
+ );
16
+
8
17
  const mockedNavigate = jest.fn();
9
18
  const mockedGoBack = jest.fn();
10
19
 
@@ -45,7 +54,7 @@ describe('test ScanChipQR', () => {
45
54
  test('create ScanChipQR', async () => {
46
55
  let tree;
47
56
  await act(async () => {
48
- tree = await create(<ScanChipQR route={route} />);
57
+ tree = await create(wrapComponent(route));
49
58
  });
50
59
  const instance = tree.root;
51
60
  const qrScan = instance.findAllByType(QRScan);
@@ -69,7 +78,7 @@ describe('test ScanChipQR', () => {
69
78
  let tree;
70
79
  const body = { id: 1, imei: 'IMEI_X', name: 'New Chip' };
71
80
  await act(async () => {
72
- tree = await create(<ScanChipQR route={route} />);
81
+ tree = await create(wrapComponent(route));
73
82
  });
74
83
  const instance = tree.root;
75
84
  const qrScan = instance.findByType(QRScan);
@@ -100,7 +109,7 @@ describe('test ScanChipQR', () => {
100
109
  let tree;
101
110
  const body = { id: 1, imei: 'IMEI_X', name: 'New Chip' };
102
111
  await act(async () => {
103
- tree = await create(<ScanChipQR route={route} />);
112
+ tree = await create(wrapComponent(route));
104
113
  });
105
114
  const instance = tree.root;
106
115
  const qrScan = instance.findByType(QRScan);
@@ -1,6 +1,9 @@
1
1
  import React from 'react';
2
2
  import { RNCamera } from 'react-native-camera';
3
3
  import { act, create } from 'react-test-renderer';
4
+
5
+ import { SCProvider } from '../../../../../context';
6
+ import { mockSCStore } from '../../../../../context/mockStore';
4
7
  import QRScan from '../index';
5
8
 
6
9
  const mockedPop = jest.fn();
@@ -17,6 +20,12 @@ jest.mock('@react-navigation/native', () => {
17
20
  };
18
21
  });
19
22
 
23
+ const wrapComponent = (data) => (
24
+ <SCProvider initState={mockSCStore({})}>
25
+ <QRScan {...data} />
26
+ </SCProvider>
27
+ );
28
+
20
29
  describe('Test QRScan', () => {
21
30
  let data;
22
31
 
@@ -32,7 +41,7 @@ describe('Test QRScan', () => {
32
41
 
33
42
  test('render QRScan', async () => {
34
43
  act(() => {
35
- tree = create(<QRScan {...data} />);
44
+ tree = create(wrapComponent(data));
36
45
  });
37
46
  const instance = tree.root;
38
47
  const RNCam = instance.findAllByType(RNCamera);
@@ -41,7 +50,7 @@ describe('Test QRScan', () => {
41
50
 
42
51
  test('onBarCodeRead', async () => {
43
52
  act(() => {
44
- tree = create(<QRScan {...data} />);
53
+ tree = create(wrapComponent(data));
45
54
  });
46
55
  const instance = tree.root;
47
56
  const RNCam = instance.findByType(RNCamera);
@@ -1,11 +1,20 @@
1
1
  import React from 'react';
2
2
  import { create, act } from 'react-test-renderer';
3
3
  import axios from 'axios';
4
+
5
+ import { SCProvider } from '../../../context';
6
+ import { mockSCStore } from '../../../context/mockStore';
4
7
  import ScanSensorQR from '..';
5
8
  import QRScan from '../../ScanChipQR/components/QRScan';
6
9
  import API from '../../../configs/API';
7
10
  import Routes from '../../../utils/Route';
8
11
 
12
+ const wrapComponent = (route) => (
13
+ <SCProvider initState={mockSCStore({})}>
14
+ <ScanSensorQR route={route} />
15
+ </SCProvider>
16
+ );
17
+
9
18
  const mockedNavigate = jest.fn();
10
19
  const mockedGoBack = jest.fn();
11
20
 
@@ -46,7 +55,7 @@ describe('test ScanSensorQR', () => {
46
55
  test('create ScanSensorQR', async () => {
47
56
  let tree;
48
57
  await act(async () => {
49
- tree = await create(<ScanSensorQR route={route} />);
58
+ tree = await create(wrapComponent(route));
50
59
  });
51
60
  const instance = tree.root;
52
61
  const qrScan = instance.findAllByType(QRScan);
@@ -70,7 +79,7 @@ describe('test ScanSensorQR', () => {
70
79
  let tree;
71
80
  const body = { id: 1, imei: 'IMEI_X', name: 'New Chip' };
72
81
  await act(async () => {
73
- tree = await create(<ScanSensorQR route={route} />);
82
+ tree = await create(wrapComponent(route));
74
83
  });
75
84
  const instance = tree.root;
76
85
  const qrScan = instance.findByType(QRScan);
@@ -103,7 +112,7 @@ describe('test ScanSensorQR', () => {
103
112
  let tree;
104
113
  const body = { id: 1, imei: 'IMEI_X', name: 'New Chip' };
105
114
  await act(async () => {
106
- tree = await create(<ScanSensorQR route={route} />);
115
+ tree = await create(wrapComponent(route));
107
116
  });
108
117
  const instance = tree.root;
109
118
  const qrScan = instance.findByType(QRScan);
@@ -16,6 +16,7 @@ import { API } from '../../../configs';
16
16
  import { TouchableOpacity } from 'react-native';
17
17
  import Routes from '../../../utils/Route';
18
18
  import WrapHeaderScrollable from '../../../commons/Sharing/WrapHeaderScrollable';
19
+ import ItemAutomate from '../../../commons/Automate/ItemAutomate';
19
20
 
20
21
  const wrapComponent = (route) => (
21
22
  <SCProvider initState={mockSCStore({})}>
@@ -271,9 +272,12 @@ describe('Test ScriptDetail', () => {
271
272
  });
272
273
  });
273
274
 
274
- const _testGoToActivityLog = (automateType, activityLogType) => {
275
+ const _testGoToActivityLog = (automateType, activityLogType, isMultiUnit) => {
275
276
  test('test go to activity log', async () => {
276
277
  route.params.type = automateType;
278
+ if (isMultiUnit) {
279
+ route.params.unit.id = undefined;
280
+ }
277
281
  await act(async () => {
278
282
  tree = await create(wrapComponent(route));
279
283
  });
@@ -288,13 +292,89 @@ describe('Test ScriptDetail', () => {
288
292
  id: route.params.id,
289
293
  type: activityLogType,
290
294
  share: route.params.unit,
295
+ filterEnabled: {
296
+ user: !isMultiUnit,
297
+ date: true,
298
+ },
291
299
  });
292
300
  });
293
301
  };
294
302
 
295
303
  _testGoToActivityLog(
296
304
  AUTOMATE_TYPE.ONE_TAP,
297
- `automate.${AUTOMATE_TYPE.ONE_TAP}`
305
+ `automate.${AUTOMATE_TYPE.ONE_TAP}`,
306
+ false
298
307
  );
299
- _testGoToActivityLog(AUTOMATE_TYPE.VALUE_CHANGE, 'automate');
308
+ _testGoToActivityLog(AUTOMATE_TYPE.VALUE_CHANGE, 'automate', false);
309
+ _testGoToActivityLog(AUTOMATE_TYPE.VALUE_CHANGE, 'automate', true);
310
+
311
+ test('Test render textCondition value change >', async () => {
312
+ route.params = {
313
+ ...route.params,
314
+ type: AUTOMATE_TYPE.VALUE_CHANGE,
315
+ automate: {
316
+ repeat: 'once',
317
+ date_repeat: '2022-01-02',
318
+ time_repeat: '09:10:00',
319
+ weekday_repeat: ['1', '2', '4', '6'],
320
+ config_name: 'Light Value',
321
+ value: 3,
322
+ condition: '>',
323
+ },
324
+ };
325
+ await act(() => {
326
+ tree = create(wrapComponent(route));
327
+ });
328
+ const instance = tree.root;
329
+ const itemAutomate = instance.findByType(ItemAutomate);
330
+ expect(itemAutomate.props.textCondition).toEqual(
331
+ 'Light Value higher than 3'
332
+ );
333
+ });
334
+
335
+ test('Test render textCondition value change =', async () => {
336
+ route.params = {
337
+ ...route.params,
338
+ type: AUTOMATE_TYPE.VALUE_CHANGE,
339
+ automate: {
340
+ repeat: 'once',
341
+ date_repeat: '2022-01-02',
342
+ time_repeat: '09:10:00',
343
+ weekday_repeat: ['1', '2', '4', '6'],
344
+ config_name: 'Light Value',
345
+ value: 3,
346
+ condition: '=',
347
+ },
348
+ };
349
+ await act(() => {
350
+ tree = create(wrapComponent(route));
351
+ });
352
+ const instance = tree.root;
353
+ const itemAutomate = instance.findByType(ItemAutomate);
354
+ expect(itemAutomate.props.textCondition).toEqual('Light Value equal 3');
355
+ });
356
+
357
+ test('Test render textCondition value change <', async () => {
358
+ route.params = {
359
+ ...route.params,
360
+ type: AUTOMATE_TYPE.VALUE_CHANGE,
361
+ automate: {
362
+ repeat: 'once',
363
+ date_repeat: '2022-01-02',
364
+ time_repeat: '09:10:00',
365
+ weekday_repeat: ['1', '2', '4', '6'],
366
+ config_name: 'Light Value',
367
+ value: 3,
368
+ condition: '<',
369
+ },
370
+ };
371
+ await act(() => {
372
+ tree = create(wrapComponent(route));
373
+ });
374
+ const instance = tree.root;
375
+ const itemAutomate = instance.findByType(ItemAutomate);
376
+ expect(itemAutomate.props.textCondition).toEqual(
377
+ 'Light Value lower than 3'
378
+ );
379
+ });
300
380
  });
@@ -63,6 +63,10 @@ const useStateAlertAction = () => {
63
63
  ? `automate.${AUTOMATE_TYPE.ONE_TAP}`
64
64
  : 'automate',
65
65
  share: unit,
66
+ filterEnabled: {
67
+ date: true,
68
+ user: Boolean(unit.id),
69
+ },
66
70
  });
67
71
  } else {
68
72
  setStateAlertAction((action) => {
@@ -43,6 +43,7 @@ import { AUTOMATE_SELECT, AUTOMATE_TYPE } from '../../configs/Constants';
43
43
  import { popAction } from '../../navigations/utils';
44
44
  import { TESTID } from '../../configs/Constants';
45
45
  import useKeyboardAnimated from '../../hooks/Explore/useKeyboardAnimated';
46
+ import { REPEAT_OPTIONS } from '../SetSchedule/components/RepeatOptionsPopup';
46
47
 
47
48
  const PreventDoubleTouch = withPreventDoubleClick(TouchableOpacity);
48
49
 
@@ -61,6 +62,7 @@ const ScriptDetail = ({ route }) => {
61
62
  const t = useTranslations();
62
63
  const {
63
64
  id,
65
+ automate = {},
64
66
  name = '',
65
67
  type,
66
68
  havePermission,
@@ -70,7 +72,6 @@ const ScriptDetail = ({ route }) => {
70
72
  isAutomateTab,
71
73
  isCreateNewAction,
72
74
  isMultiUnits,
73
- textCondition = '',
74
75
  } = params;
75
76
  const [isStar, setIsStar] = useState(false);
76
77
  const [scriptName, setScriptName] = useState(name);
@@ -214,15 +215,15 @@ const ScriptDetail = ({ route }) => {
214
215
 
215
216
  const handleUpdateAutomate = useCallback(async () => {
216
217
  navigate(Routes.AddNewAutoSmart, {
217
- type: AUTOMATE_TYPE.AUTOMATE,
218
- automateId: id,
218
+ type,
219
+ automate,
219
220
  unit,
220
221
  isAutomateTab,
221
222
  isMultiUnits,
222
223
  scriptName: name,
223
224
  });
224
225
  // eslint-disable-next-line react-hooks/exhaustive-deps
225
- }, [name]);
226
+ }, [name, automate]);
226
227
 
227
228
  const onGoBack = useCallback(() => {
228
229
  if (isCreateScriptSuccess || isCreateNewAction) {
@@ -351,6 +352,66 @@ const ScriptDetail = ({ route }) => {
351
352
  return () => backHandler.remove();
352
353
  }, [isCreateScriptSuccess]);
353
354
  const isHaveScriptActions = data?.length > 0;
355
+
356
+ const textCondition = useMemo(() => {
357
+ const {
358
+ condition,
359
+ config_name,
360
+ value,
361
+ repeat,
362
+ date_repeat,
363
+ time_repeat,
364
+ weekday_repeat,
365
+ } = automate;
366
+ if (type === AUTOMATE_TYPE.VALUE_CHANGE) {
367
+ let textCondition;
368
+ if (condition === '>') {
369
+ textCondition = 'higher_than';
370
+ } else if (condition === '<') {
371
+ textCondition = 'lower_than';
372
+ } else if (condition === '=') {
373
+ textCondition = 'equal';
374
+ }
375
+ return `${config_name} ${t(textCondition)} ${value}`;
376
+ } else if (type === AUTOMATE_TYPE.SCHEDULE) {
377
+ const time =
378
+ time_repeat.length >= 8
379
+ ? time_repeat.substring(0, time_repeat.length - 3)
380
+ : time_repeat;
381
+ const date = date_repeat.split('-').reverse().join('/');
382
+ const weekday = {
383
+ 1: t('mon'),
384
+ 2: t('tue'),
385
+ 3: t('wed'),
386
+ 4: t('thu'),
387
+ 5: t('fri'),
388
+ 6: t('sat'),
389
+ 0: t('sun'),
390
+ };
391
+
392
+ if (repeat === REPEAT_OPTIONS.ONCE) {
393
+ return `${time} ${date}`;
394
+ } else if (repeat === REPEAT_OPTIONS.EVERYDAY) {
395
+ return `${t('every_day_at', { time })}`;
396
+ } else if (repeat === REPEAT_OPTIONS.EVERYWEEK) {
397
+ //sort 0 is last number, exp: [1,3,5,0]
398
+ const newWeekdayRepeat = weekday_repeat.map((item) => parseInt(item));
399
+ const sortWeekday = newWeekdayRepeat.sort((a, b) => {
400
+ if (a !== 0 && b === 0) {
401
+ return -1;
402
+ }
403
+ if (a === 0 && b !== 0) {
404
+ return 1;
405
+ }
406
+ return a - b;
407
+ });
408
+ const textWeekday = sortWeekday.map((item) => weekday[item]).join(', ');
409
+ return `${textWeekday} ${t('at')} ${time}`;
410
+ }
411
+ }
412
+ return null;
413
+ }, [t, automate, type]);
414
+
354
415
  return (
355
416
  <View style={styles.wrap}>
356
417
  <WrapHeaderScrollable
@@ -28,7 +28,7 @@ test('test select', async () => {
28
28
  await act(async () => {
29
29
  await items[0].props.onPress();
30
30
  });
31
- expect(mockSetWeekday).toBeCalledWith(['0']);
31
+ expect(mockSetWeekday).toBeCalledWith(['1']);
32
32
 
33
33
  mockSetWeekday.mockClear();
34
34
  props = {
@@ -44,5 +44,5 @@ test('test select', async () => {
44
44
  await act(async () => {
45
45
  await items[0].props.onPress();
46
46
  });
47
- expect(mockSetWeekday).toBeCalledWith([]);
47
+ expect(mockSetWeekday).toBeCalledWith(['0', '1']);
48
48
  });
@@ -162,6 +162,14 @@ describe('Test SetSchedule', () => {
162
162
  isAutomateTab: true,
163
163
  isScript: undefined,
164
164
  isMultiUnits: undefined,
165
+ automateId: undefined,
166
+ scriptName: undefined,
167
+ automate: {
168
+ date_repeat: '2021-01-24',
169
+ repeat: 'once',
170
+ time_repeat: '12:00:00',
171
+ weekday_repeat: [],
172
+ },
165
173
  });
166
174
  });
167
175
  });
@@ -5,7 +5,15 @@ import { Colors } from '../../../configs';
5
5
  import styles from '../styles/SelectWeekdayStyles';
6
6
  import { useTranslations } from '../../../hooks/Common/useTranslations';
7
7
 
8
- const WEEKDAY_ITEMS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
8
+ const WEEKDAY_ITEMS = [
9
+ { day: 'Mon', value: 1 },
10
+ { day: 'Tue', value: 2 },
11
+ { day: 'Wed', value: 3 },
12
+ { day: 'Thu', value: 4 },
13
+ { day: 'Fri', value: 5 },
14
+ { day: 'Sat', value: 6 },
15
+ { day: 'Sun', value: 0 },
16
+ ];
9
17
 
10
18
  const SelectWeekday = ({ weekday, setWeekday }) => {
11
19
  const t = useTranslations();
@@ -23,11 +31,10 @@ const SelectWeekday = ({ weekday, setWeekday }) => {
23
31
  );
24
32
 
25
33
  const WeekdayItem = useCallback(
26
- ({ item, index, isSelected }) => {
34
+ ({ item, isSelected }) => {
27
35
  return (
28
36
  <TouchableOpacity
29
- key={index}
30
- onPress={() => onSetWeekday(`${index}`)}
37
+ onPress={() => onSetWeekday(`${item?.value}`)}
31
38
  style={[styles.item, isSelected && styles.selected]}
32
39
  >
33
40
  <Text
@@ -36,7 +43,7 @@ const SelectWeekday = ({ weekday, setWeekday }) => {
36
43
  style={styles.text}
37
44
  semibold
38
45
  >
39
- {item}
46
+ {item?.day}
40
47
  </Text>
41
48
  </TouchableOpacity>
42
49
  );
@@ -54,8 +61,7 @@ const SelectWeekday = ({ weekday, setWeekday }) => {
54
61
  <WeekdayItem
55
62
  key={index}
56
63
  item={item}
57
- index={index}
58
- isSelected={weekday.includes(`${index}`)}
64
+ isSelected={weekday.includes(`${item?.value}`)}
59
65
  />
60
66
  ))}
61
67
  </View>