@eohjsc/react-native-smart-city 0.7.3-rc10 → 0.7.3-rc12

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 (29) hide show
  1. package/package.json +1 -1
  2. package/src/commons/Action/__test__/ItemQuickAction.test.js +2 -2
  3. package/src/commons/ActionGroup/__test__/index.test.js +2 -2
  4. package/src/commons/DateTimeRangeChange/DateTimeButton.js +1 -1
  5. package/src/commons/Device/HorizontalBarChart.js +54 -34
  6. package/src/commons/Grid/GridItem.js +10 -2
  7. package/src/commons/IconComponent/index.js +4 -4
  8. package/src/commons/OneTapTemplate/__test__/StatesGridActionTemplate.test.js +4 -4
  9. package/src/configs/Theme.js +1 -1
  10. package/src/navigations/SharedStack.js +2 -0
  11. package/src/screens/Automate/AddNewAction/ChooseAction.js +17 -3
  12. package/src/screens/Automate/AddNewAction/ChooseConfig.js +7 -6
  13. package/src/screens/Automate/AddNewAction/SelectControlDevices.js +27 -17
  14. package/src/screens/Automate/AddNewAction/SelectMonitorDevices.js +17 -10
  15. package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +35 -32
  16. package/src/screens/Automate/AddNewAction/SetupScriptDelay.js +1 -1
  17. package/src/screens/Automate/AddNewAction/SetupScriptNotify.js +3 -3
  18. package/src/screens/Automate/AddNewAction/Styles/SetupSensorStyles.js +20 -4
  19. package/src/screens/Automate/AddNewAction/__test__/ChooseAction.test.js +1 -1
  20. package/src/screens/Automate/AddNewAction/__test__/SetupConfigCondition.test.js +12 -11
  21. package/src/screens/Automate/AddNewAction/__test__/SetupScriptNotify.test.js +29 -0
  22. package/src/screens/Automate/AddNewAutoSmart/AddTypeSmart.js +7 -7
  23. package/src/screens/Automate/ScriptDetail/Components/AddActionScript.js +12 -4
  24. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +2 -0
  25. package/src/screens/Automate/SetSchedule/components/RowItem.js +5 -5
  26. package/src/screens/Notification/components/NotificationItem.js +2 -2
  27. package/src/screens/SelectUnit/index.js +1 -1
  28. package/src/utils/I18n/translations/en.js +1 -1
  29. package/src/utils/I18n/translations/vi.js +3 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eohjsc/react-native-smart-city",
3
3
  "title": "React Native Smart Home",
4
- "version": "0.7.3-rc10",
4
+ "version": "0.7.3-rc12",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -1,8 +1,8 @@
1
+ import { IconOutline } from '@ant-design/icons-react-native';
1
2
  import { factory } from 'factory-girl';
2
3
  import React from 'react';
3
4
  import { TouchableOpacity } from 'react-native';
4
5
  import Toast from 'react-native-toast-message';
5
- import AntDesign from 'react-native-vector-icons/AntDesign';
6
6
  import { act, create } from 'react-test-renderer';
7
7
  import { Colors } from '../../../configs';
8
8
  import { AccessibilityLabel } from '../../../configs/Constants';
@@ -94,7 +94,7 @@ describe('Test ItemQuickAction', () => {
94
94
  await buttonOnActionPress.props.onPress();
95
95
  });
96
96
 
97
- const icon = instance.findByType(AntDesign);
97
+ const icon = instance.findByType(IconOutline);
98
98
  expect(icon.props.color).toEqual(Colors.TextGray);
99
99
  });
100
100
 
@@ -413,7 +413,7 @@ describe('Test ActionGroup', () => {
413
413
  config: 5,
414
414
  is_on_value: 0,
415
415
  action: 'action1-47b3-43f6-ba45-83cd1cf5d8f2',
416
- icon: 'upcircle',
416
+ icon: 'up-circle',
417
417
  text: 'Text',
418
418
  action_data: actionData,
419
419
  },
@@ -421,7 +421,7 @@ describe('Test ActionGroup', () => {
421
421
  config: 6,
422
422
  is_on_value: 0,
423
423
  action: 'action2-47b3-43f6-ba45-83cd1cf5d8f2',
424
- icon: 'upcircle',
424
+ icon: 'up-circle',
425
425
  text: 'Text',
426
426
  action_data: actionData,
427
427
  },
@@ -25,7 +25,7 @@ const styles = StyleSheet.create({
25
25
  marginHorizontal: 8,
26
26
  flexDirection: 'row',
27
27
  justifyContent: 'space-between',
28
- width: 150,
28
+ width: 130,
29
29
  alignItems: 'center',
30
30
  },
31
31
  txtTime: {
@@ -1,11 +1,46 @@
1
- import React, { memo, useEffect, useMemo, useState } from 'react';
2
- import { View, StyleSheet } from 'react-native';
3
1
  import { isEmpty } from 'lodash';
2
+ import React, { memo, useEffect, useMemo, useState } from 'react';
3
+ import { StyleSheet, View } from 'react-native';
4
4
 
5
5
  import { Colors } from '../../configs';
6
6
  import { getMaxValueIndex } from '../../utils/chartHelper/getMaxValueIndex';
7
7
  import Highcharts from '../Highcharts';
8
8
 
9
+ // https://github.com/facebook/hermes/issues/114#issuecomment-887106990
10
+ // 'show source' unstable, work sometime, use this to generate formatter function
11
+
12
+ // function formatter() {
13
+ // 'show source';
14
+ // const { unit, price } = JSON.parse(this.series.name);
15
+ // const textColor =
16
+ // this.color === '#00979D'
17
+ // ? this.y === 0
18
+ // ? '#262626'
19
+ // : '#FFFFFF'
20
+ // : '#262626';
21
+ // const valueStyle = `color:${textColor};font-weight:normal;font-size:12px;`;
22
+ // const costStyle = valueStyle + 'font-weight:bold;';
23
+ // let label = `<span style="${valueStyle}">` + `${this.y}${unit}`;
24
+ // if (price === '' || price === null || isNaN(price)) {
25
+ // return label + '</span>';
26
+ // }
27
+ // const formatMoney = (number) => {
28
+ // const formatNumber = number.toFixed();
29
+ // return (
30
+ // parseInt(formatNumber, 10)
31
+ // .toFixed(0)
32
+ // .toString()
33
+ // .replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1.') + 'đ'
34
+ // );
35
+ // };
36
+ // return (
37
+ // label +
38
+ // `/<span style="${costStyle}">` +
39
+ // formatMoney(this.y * price) +
40
+ // '</span></span>'
41
+ // );
42
+ // }
43
+
9
44
  const HorizontalBarChart = memo(({ datas, config }) => {
10
45
  const [chartOptions, setChartOptions] = useState({
11
46
  chart: {
@@ -57,38 +92,23 @@ const HorizontalBarChart = memo(({ datas, config }) => {
57
92
  dataLabels: {
58
93
  enabled: true,
59
94
  useHTML: true,
60
- formatter: function () {
61
- const { unit, price } = JSON.parse(this.series.name);
62
- const textColor =
63
- this.color === '#00979D'
64
- ? this.y === 0
65
- ? '#262626'
66
- : '#FFFFFF'
67
- : '#262626';
68
- const valueStyle = `color:${textColor};font-weight:normal;font-size:12px;`;
69
- const costStyle = valueStyle + 'font-weight:bold;';
70
-
71
- let label = `<span style="${valueStyle}">` + `${this.y}${unit}`;
72
- if (price === '' || price === null || isNaN(price)) {
73
- return label + '</span>';
74
- }
75
-
76
- const formatMoney = (number) => {
77
- const formatNumber = number.toFixed();
78
- return (
79
- parseInt(formatNumber, 10)
80
- .toFixed(0)
81
- .toString()
82
- .replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1.') + 'đ'
83
- );
84
- };
85
- return (
86
- label +
87
- `/<span style="${costStyle}">` +
88
- formatMoney(this.y * price) +
89
- '</span></span>'
90
- );
91
- },
95
+ formatter: `function formatter() {
96
+ var _JSON$parse = JSON.parse(this.series.name),
97
+ unit = _JSON$parse.unit,
98
+ price = _JSON$parse.price;
99
+ var textColor = this.color === '#00979D' ? this.y === 0 ? '#262626' : '#FFFFFF' : '#262626';
100
+ var valueStyle = "color:" + textColor + ";font-weight:normal;font-size:12px;";
101
+ var costStyle = valueStyle + 'font-weight:bold;';
102
+ var label = "<span style=\\"" + valueStyle + "\\">" + ("" + this.y + unit);
103
+ if (price === '' || price === null || isNaN(price)) {
104
+ return label + '</span>';
105
+ }
106
+ var formatMoney = function formatMoney(number) {
107
+ var formatNumber = number.toFixed();
108
+ return parseInt(formatNumber, 10).toFixed(0).toString().replace(/(\\d)(?=(\\d\\d\\d)+(?!\\d))/g, '$1.') + 'đ';
109
+ };
110
+ return label + ("/<span style=\\"" + costStyle + "\\">") + formatMoney(this.y * price) + '</span></span>';
111
+ }`,
92
112
  align: 'right',
93
113
  inside: false,
94
114
  },
@@ -3,7 +3,15 @@ import { TouchableWithoutFeedback, View } from 'react-native';
3
3
  import styles from './GridItemStyles';
4
4
 
5
5
  const GridItem = memo(
6
- ({ icon, accessibilityLabel, selected, onPress, item, children }) => {
6
+ ({
7
+ icon,
8
+ accessibilityLabel,
9
+ selected,
10
+ onPress,
11
+ item,
12
+ children,
13
+ style = {},
14
+ }) => {
7
15
  const isActive = selected && styles.active;
8
16
 
9
17
  return (
@@ -11,7 +19,7 @@ const GridItem = memo(
11
19
  onPress={() => onPress && onPress(item)}
12
20
  accessibilityLabel={accessibilityLabel}
13
21
  >
14
- <View style={[styles.container, isActive]}>{children}</View>
22
+ <View style={[styles.container, style, isActive]}>{children}</View>
15
23
  </TouchableWithoutFeedback>
16
24
  );
17
25
  }
@@ -1,4 +1,4 @@
1
- import AntDesign from 'react-native-vector-icons/AntDesign';
1
+ import { IconFill, IconOutline } from '@ant-design/icons-react-native';
2
2
  import React, { memo, useMemo } from 'react';
3
3
  import { StyleSheet } from 'react-native';
4
4
  import { Colors } from '../../configs';
@@ -30,7 +30,7 @@ const IconComponent = memo(
30
30
  } else if (newIcon.includes('TwoTone')) {
31
31
  newIcon = newIcon.substring(0, newIcon.length - 'TwoTone'.length);
32
32
  }
33
- newIcon = newIcon.replace(/[A-Z]/g, (m) => m.toLowerCase());
33
+ newIcon = newIcon.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
34
34
  }
35
35
  return newIcon;
36
36
  }, [icon]);
@@ -62,7 +62,7 @@ const IconComponent = memo(
62
62
  );
63
63
  } else if (icon.includes('Filled')) {
64
64
  return (
65
- <AntDesign
65
+ <IconFill
66
66
  name={displayIcon}
67
67
  color={colorIcon}
68
68
  size={size}
@@ -76,7 +76,7 @@ const IconComponent = memo(
76
76
  ? 'poweroff'
77
77
  : displayIcon;
78
78
  return (
79
- <AntDesign
79
+ <IconOutline
80
80
  name={iconCT}
81
81
  color={colorIcon}
82
82
  size={size}
@@ -27,7 +27,7 @@ describe('Test StatesGridActionTemplate', () => {
27
27
  config: 1024,
28
28
  is_on_value: 1,
29
29
  action: '800ff454-4e2a-4a38-bad6-1bded728193e',
30
- icon: 'upcircle',
30
+ icon: 'up-circle',
31
31
  icon_kit: 41,
32
32
  text: 'Auto',
33
33
  },
@@ -35,7 +35,7 @@ describe('Test StatesGridActionTemplate', () => {
35
35
  config: 1024,
36
36
  is_on_value: 2,
37
37
  action: '4e43da81-469e-4d23-a66b-2656db7cf196',
38
- icon: 'upcircle',
38
+ icon: 'up-circle',
39
39
  icon_kit: 42,
40
40
  text: 'Cool',
41
41
  },
@@ -43,7 +43,7 @@ describe('Test StatesGridActionTemplate', () => {
43
43
  config: 1024,
44
44
  is_on_value: 3,
45
45
  action: '63f1bbfa-0e42-4401-9ea2-4aa07327ff26',
46
- icon: 'upcircle',
46
+ icon: 'up-circle',
47
47
  icon_kit: 44,
48
48
  text: 'Dry',
49
49
  },
@@ -51,7 +51,7 @@ describe('Test StatesGridActionTemplate', () => {
51
51
  config: 1024,
52
52
  is_on_value: 4,
53
53
  action: '8ba3e471-dd84-478b-87f3-6008aead8804',
54
- icon: 'upcircle',
54
+ icon: 'up-circle',
55
55
  icon_kit: 43,
56
56
  text: 'Fan Only',
57
57
  },
@@ -86,7 +86,7 @@ export default StyleSheet.create({
86
86
  alignItems: 'center',
87
87
  },
88
88
  menuIcon: {
89
- paddingLeft: 16,
89
+ paddingRight: 10,
90
90
  height: '100%',
91
91
  justifyContent: 'center',
92
92
  },
@@ -50,6 +50,8 @@ export default SharedStack;
50
50
 
51
51
  const styles = StyleSheet.create({
52
52
  btnMenu: {
53
+ paddingLeft: 0,
54
+ paddingRight: 10,
53
55
  paddingHorizontal: 16,
54
56
  height: '100%',
55
57
  justifyContent: 'center',
@@ -14,7 +14,13 @@ import RenderActionItem from './RenderActionItem';
14
14
  const ChooseAction = ({ route }) => {
15
15
  const t = useTranslations();
16
16
  const { navigate } = useNavigation();
17
- const { unitId, device, automateId, numberActionCanAdd } = route?.params;
17
+ const {
18
+ unitId,
19
+ device,
20
+ automateId,
21
+ numberActionCanAdd,
22
+ multiUnit = {},
23
+ } = route.params;
18
24
  const [data, setData] = useState([]);
19
25
  const [actions, setActions] = useState([]);
20
26
 
@@ -48,7 +54,7 @@ const ChooseAction = ({ route }) => {
48
54
  API.AUTOMATE.ADD_SCRIPT_ACTION(automateId),
49
55
  {
50
56
  list_action,
51
- unit: unitId,
57
+ unit: unitId || multiUnit.id,
52
58
  }
53
59
  );
54
60
 
@@ -58,7 +64,15 @@ const ChooseAction = ({ route }) => {
58
64
  merge: true,
59
65
  params: { saveAt: moment().valueOf() },
60
66
  });
61
- }, [actions, numberActionCanAdd, automateId, unitId, navigate, t]);
67
+ }, [
68
+ actions,
69
+ numberActionCanAdd,
70
+ automateId,
71
+ unitId,
72
+ multiUnit.id,
73
+ navigate,
74
+ t,
75
+ ]);
62
76
 
63
77
  const handleOnSelectAction = (action) => {
64
78
  setActions((prevActions) => {
@@ -1,17 +1,17 @@
1
+ import { useNavigation } from '@react-navigation/native';
1
2
  import React, { useCallback, useEffect, useState } from 'react';
2
3
  import { View } from 'react-native';
3
- import { useNavigation } from '@react-navigation/native';
4
4
 
5
- import { useTranslations } from '../../../hooks/Common/useTranslations';
5
+ import GridItem from '../../../commons/Grid/GridItem';
6
6
  import Text from '../../../commons/Text';
7
- import { axiosGet } from '../../../utils/Apis/axios';
8
7
  import { API, Colors } from '../../../configs';
8
+ import { AUTOMATE_TYPE } from '../../../configs/Constants';
9
+ import { useTranslations } from '../../../hooks/Common/useTranslations';
10
+ import { axiosGet } from '../../../utils/Apis/axios';
9
11
  import Routes from '../../../utils/Route';
10
- import styles from './Styles/SelectActionStyles';
11
12
  import { LoadingSelectAction } from './Components';
12
- import { AUTOMATE_TYPE } from '../../../configs/Constants';
13
13
  import NewActionWrapper from './NewActionWrapper';
14
- import GridItem from '../../../commons/Grid/GridItem';
14
+ import styles from './Styles/SelectActionStyles';
15
15
 
16
16
  const ChooseConfig = ({ route }) => {
17
17
  const t = useTranslations();
@@ -44,6 +44,7 @@ const ChooseConfig = ({ route }) => {
44
44
  config,
45
45
  automate,
46
46
  closeScreen,
47
+ unitId: automate?.unit,
47
48
  });
48
49
  },
49
50
  [automate, closeScreen, navigate]
@@ -8,8 +8,13 @@ import SelectDevices from './Components/SelectDevices';
8
8
 
9
9
  const SelectControlDevices = ({ route }) => {
10
10
  const t = useTranslations();
11
- const { unitId, closeScreen, automateId, numberActionCanAdd } =
12
- route?.params || {};
11
+ const {
12
+ unitId,
13
+ closeScreen,
14
+ automateId,
15
+ numberActionCanAdd,
16
+ multiUnit = {},
17
+ } = route.params || {};
13
18
 
14
19
  const [stations, setStations] = useState([]);
15
20
  const [listStation, setListStation] = useState([]);
@@ -19,22 +24,26 @@ const SelectControlDevices = ({ route }) => {
19
24
  const [loading, setLoading] = useState(true);
20
25
 
21
26
  const fetchDetails = useCallback(async () => {
22
- await fetchWithCache(API.UNIT.DEVICE_CONTROL(unitId), {}, (response) => {
23
- const { success, data } = response;
24
- if (success) {
25
- const newData = data.filter((item) => item.sensors.length > 0);
26
- const listMenu = newData.map((item, index) => ({
27
- text: item.name,
28
- station: item,
29
- index: index,
30
- }));
31
- setStations(newData);
32
- setListMenuItem(listMenu);
33
- setListStation(listMenu);
27
+ await fetchWithCache(
28
+ API.UNIT.DEVICE_CONTROL(unitId || multiUnit.id),
29
+ {},
30
+ (response) => {
31
+ const { success, data } = response;
32
+ if (success) {
33
+ const newData = data.filter((item) => item.sensors.length > 0);
34
+ const listMenu = newData.map((item, index) => ({
35
+ text: item.name,
36
+ station: item,
37
+ index: index,
38
+ }));
39
+ setStations(newData);
40
+ setListMenuItem(listMenu);
41
+ setListStation(listMenu);
42
+ }
34
43
  }
35
- });
44
+ );
36
45
  setLoading(false);
37
- }, [unitId]);
46
+ }, [multiUnit.id, unitId]);
38
47
 
39
48
  useEffect(() => {
40
49
  fetchDetails();
@@ -48,9 +57,10 @@ const SelectControlDevices = ({ route }) => {
48
57
  device: selectedDevice,
49
58
  closeScreen,
50
59
  numberActionCanAdd,
60
+ multiUnit,
51
61
  });
52
62
  },
53
- [navigate, unitId, automateId, closeScreen, numberActionCanAdd]
63
+ [navigate, unitId, automateId, closeScreen, numberActionCanAdd, multiUnit]
54
64
  );
55
65
 
56
66
  if (loading) {
@@ -1,16 +1,22 @@
1
- import React, { useCallback, useEffect, useState } from 'react';
2
1
  import { useNavigation } from '@react-navigation/native';
2
+ import React, { useCallback, useEffect, useState } from 'react';
3
3
 
4
+ import { API } from '../../../configs';
5
+ import { AUTOMATE_TYPE } from '../../../configs/Constants';
4
6
  import { useTranslations } from '../../../hooks/Common/useTranslations';
5
7
  import { fetchWithCache } from '../../../utils/Apis/axios';
6
- import { API } from '../../../configs';
7
8
  import Routes from '../../../utils/Route';
8
9
  import SelectDevices from './Components/SelectDevices';
9
- import { AUTOMATE_TYPE } from '../../../configs/Constants';
10
10
 
11
11
  const SelectMonitorDevices = ({ route }) => {
12
12
  const t = useTranslations();
13
- const { automate = {}, isCreateNewAction, closeScreen } = route?.params || {};
13
+ const {
14
+ automate = {},
15
+ isCreateNewAction,
16
+ closeScreen,
17
+ multiUnit = {},
18
+ } = route.params || {};
19
+ const { type, unit, sensor_id } = automate;
14
20
 
15
21
  const [stations, setStations] = useState([]);
16
22
  const [listStation, setListStation] = useState([]);
@@ -22,11 +28,11 @@ const SelectMonitorDevices = ({ route }) => {
22
28
 
23
29
  const fetchDetails = useCallback(async () => {
24
30
  const configs = { params: {} };
25
- if (automate?.type === AUTOMATE_TYPE.EVENT) {
31
+ if (type === AUTOMATE_TYPE.EVENT) {
26
32
  configs.params.type = 'event';
27
33
  }
28
34
  await fetchWithCache(
29
- API.UNIT.DEVICE_SENSOR(automate?.unit),
35
+ API.UNIT.DEVICE_SENSOR(unit || multiUnit.id),
30
36
  configs,
31
37
  (response) => {
32
38
  const { success, data } = response;
@@ -46,17 +52,17 @@ const SelectMonitorDevices = ({ route }) => {
46
52
  }
47
53
  );
48
54
  setLoading(false);
49
- }, [automate?.type, automate?.unit]);
55
+ }, [multiUnit.id, type, unit]);
50
56
 
51
57
  useEffect(() => {
52
- if (!automate?.sensor_id) {
58
+ if (!sensor_id) {
53
59
  return;
54
60
  }
55
61
  let selectSensor = {};
56
62
  let found = false;
57
63
  for (let i = 0; i < stations.length; i++) {
58
64
  for (let j = 0; j < stations[i].sensors.length; j++) {
59
- if (stations[i].sensors[j].id === automate.sensor_id) {
65
+ if (stations[i].sensors[j].id === sensor_id) {
60
66
  selectSensor = stations[i].sensors[j];
61
67
  found = true;
62
68
  break;
@@ -67,7 +73,7 @@ const SelectMonitorDevices = ({ route }) => {
67
73
  }
68
74
  }
69
75
  setDefaultSelectedDevice(selectSensor);
70
- }, [automate?.sensor_id, stations]);
76
+ }, [sensor_id, stations]);
71
77
 
72
78
  useEffect(() => {
73
79
  fetchDetails();
@@ -86,6 +92,7 @@ const SelectMonitorDevices = ({ route }) => {
86
92
  stationName: stations[indexStation]?.name,
87
93
  isCreateNewAction,
88
94
  closeScreen,
95
+ unitId: automate?.unit,
89
96
  });
90
97
  },
91
98
  [navigate, automate, stations, isCreateNewAction, closeScreen]
@@ -1,17 +1,17 @@
1
1
  import React, { useMemo, useState } from 'react';
2
- import { View, TouchableOpacity, FlatList } from 'react-native';
2
+ import { View } from 'react-native';
3
3
 
4
- import styles from './Styles/SetupSensorStyles';
5
- import { useTranslations } from '../../../hooks/Common/useTranslations';
6
- import Text from '../../../commons/Text';
7
4
  import { useNavigation, useRoute } from '@react-navigation/native';
8
- import NewActionWrapper from './NewActionWrapper';
5
+ import { Button } from '../../../commons';
6
+ import TextInput from '../../../commons/Form/TextInput';
9
7
  import GridItem from '../../../commons/Grid/GridItem';
10
- import Routes from '../../../utils/Route';
11
8
  import ModalCustom from '../../../commons/Modal/ModalCustom';
9
+ import Text from '../../../commons/Text';
10
+ import { useTranslations } from '../../../hooks/Common/useTranslations';
11
+ import Routes from '../../../utils/Route';
12
12
  import { ToastBottomHelper } from '../../../utils/Utils';
13
- import CheckBox from '@react-native-community/checkbox';
14
- import TextInput from '../../../commons/Form/TextInput';
13
+ import NewActionWrapper from './NewActionWrapper';
14
+ import styles from './Styles/SetupSensorStyles';
15
15
 
16
16
  const valueEvaluationToOptions = (valueEvaluation) => {
17
17
  if (valueEvaluation.template === 'range') {
@@ -54,7 +54,7 @@ const SetupConfigCondition = () => {
54
54
  value: '<',
55
55
  },
56
56
  {
57
- label: `${t('is')} (=)`,
57
+ label: `${t('is_equal')} (=)`,
58
58
  value: '=',
59
59
  },
60
60
  {
@@ -132,41 +132,44 @@ const SetupConfigCondition = () => {
132
132
  style={styles.modal}
133
133
  >
134
134
  <View style={styles.modalContent}>
135
- <Text type="Body">
135
+ <Text type="Body" bold style={styles.titleCondition}>
136
136
  {t('when_value_is', { config_name: config.name })}
137
137
  </Text>
138
- <FlatList
139
- renderHeader={t('condition')}
140
- data={normalConditions}
141
- renderItem={({ item }) => {
138
+ <View style={styles.flatListCondition}>
139
+ {normalConditions.map((item) => {
142
140
  return (
143
- <View>
144
- <Text>{item.label}</Text>
145
- <CheckBox
146
- onChange={() => {
147
- automate.condition = item.value;
148
- setCustomCondition(item.value);
149
- }}
150
- value={item.value === customCondition}
151
- />
152
- </View>
141
+ <GridItem
142
+ onPress={() => {
143
+ automate.condition = item.value;
144
+ setCustomCondition(item.value);
145
+ }}
146
+ selected={item.value === customCondition}
147
+ style={styles.gridItem}
148
+ >
149
+ <Text type="Body">{item.label}</Text>
150
+ </GridItem>
153
151
  );
154
- }}
155
- />
152
+ })}
153
+ </View>
154
+
156
155
  <View style={styles.inputValue}>
157
- <Text>{t('value')}</Text>
158
156
  <TextInput
159
157
  keyboardType="decimal-pad"
160
158
  type="number"
161
159
  maxLength={10}
160
+ placeholder={t('value')}
162
161
  onChange={(value) => {
163
162
  const floatValue = parseFloat(value.replace(',', '.'));
164
163
  automate.value = floatValue;
165
164
  }}
166
- style={styles.numberInput}
165
+ textInputStyle={styles.numberInput}
166
+ wrapStyle={styles.wrapTextInput}
167
167
  />
168
168
  </View>
169
- <TouchableOpacity
169
+
170
+ <Button
171
+ type="primary"
172
+ title={t('continue')}
170
173
  testID={'continue_button'}
171
174
  onPress={() => {
172
175
  if (
@@ -179,9 +182,9 @@ const SetupConfigCondition = () => {
179
182
  setIsShowModal(false);
180
183
  navigate(Routes.ValueChangeName, { automate, closeScreen });
181
184
  }}
182
- >
183
- <Text>{t('continue')}</Text>
184
- </TouchableOpacity>
185
+ style={styles.buttonContinue}
186
+ textSemiBold={false}
187
+ />
185
188
  </View>
186
189
  </ModalCustom>
187
190
  </NewActionWrapper>
@@ -40,7 +40,7 @@ const SetupScriptDelay = ({ route }) => {
40
40
  params: { saveAt: moment().valueOf() },
41
41
  });
42
42
  } else {
43
- ToastBottomHelper.error(t('text_done'));
43
+ ToastBottomHelper.error(t('error_please_try_later'));
44
44
  }
45
45
  }, [automateId, navigate, delay, t]);
46
46
 
@@ -17,9 +17,9 @@ import moment from 'moment';
17
17
  const SetupScriptNotify = ({ route }) => {
18
18
  const t = useTranslations();
19
19
  const { goBack, navigate } = useNavigation();
20
- const { automate = {}, unitId } = route?.params || {};
20
+ const { automate = {}, unitId, multiUnit } = route.params || {};
21
21
  const { id: automateId } = automate;
22
- const [notify, setNotify] = useState({ unit: unitId });
22
+ const [notify, setNotify] = useState({ unit: unitId || multiUnit.id });
23
23
 
24
24
  const onChangeTitle = (value) => {
25
25
  setNotify((state) => ({
@@ -47,7 +47,7 @@ const SetupScriptNotify = ({ route }) => {
47
47
  params: { saveAt: moment().valueOf() },
48
48
  });
49
49
  } else {
50
- ToastBottomHelper.error(t('text_done'));
50
+ ToastBottomHelper.error(t('error_please_try_later'));
51
51
  }
52
52
  }, [automateId, navigate, notify, t]);
53
53
 
@@ -1,5 +1,5 @@
1
1
  import { StyleSheet } from 'react-native';
2
- import { Theme, Colors } from '../../../../configs';
2
+ import { Colors, Theme } from '../../../../configs';
3
3
 
4
4
  export default StyleSheet.create({
5
5
  ...Theme,
@@ -11,10 +11,9 @@ export default StyleSheet.create({
11
11
  },
12
12
  inputValue: { marginBottom: 10 },
13
13
  numberInput: {
14
- borderRadius: 5,
14
+ borderRadius: 8,
15
15
  borderWidth: 1,
16
- height: 38,
17
- marginTop: 10,
16
+ height: 50,
18
17
  },
19
18
  boxDevices: {
20
19
  flexWrap: 'wrap',
@@ -43,4 +42,21 @@ export default StyleSheet.create({
43
42
  width: '100%',
44
43
  borderRadius: 10,
45
44
  },
45
+ titleCondition: {
46
+ marginBottom: 16,
47
+ },
48
+ wrapTextInput: {
49
+ marginTop: 0,
50
+ },
51
+ flatListCondition: {
52
+ display: 'flex',
53
+ flexDirection: 'row',
54
+ justifyContent: 'space-between',
55
+ },
56
+ gridItem: {
57
+ width: 'fit-content',
58
+ },
59
+ buttonContinue: {
60
+ flex: 0,
61
+ },
46
62
  });
@@ -162,7 +162,7 @@ describe('Test ChooseAction', () => {
162
162
  is_on_value: 1,
163
163
  action: '800ff454-4e2a-4a38-bad6-1bded728193e',
164
164
  allow_config_store_value: true,
165
- icon: 'upcircle',
165
+ icon: 'up-circle',
166
166
  icon_kit: 41,
167
167
  text: 'Auto',
168
168
  },
@@ -1,18 +1,17 @@
1
+ import { useRoute } from '@react-navigation/native';
1
2
  import React from 'react';
3
+ import { TouchableWithoutFeedback } from 'react-native';
2
4
  import { act, create } from 'react-test-renderer';
3
- import { SCProvider } from '../../../../context';
4
- import { mockSCStore } from '../../../../context/mockStore';
5
- import SetupConfigCondition from '../SetupConfigCondition';
6
- import { useRoute } from '@react-navigation/native';
7
- import { ToastBottomHelper } from '../../../../utils/Utils';
8
5
  import { Text } from '../../../../commons';
9
- import GridItem from '../../../../commons/Grid/GridItem';
10
- import { TouchableWithoutFeedback } from 'react-native';
11
- import Routes from '../../../../utils/Route';
12
- import { ModalCustom } from '../../../../commons/Modal';
13
6
  import { showAllGridItems } from '../../../../commons/ActionTemplate/__test__/utils';
14
7
  import TextInput from '../../../../commons/Form/TextInput';
15
- import CheckBox from '@react-native-community/checkbox';
8
+ import GridItem from '../../../../commons/Grid/GridItem';
9
+ import { ModalCustom } from '../../../../commons/Modal';
10
+ import { SCProvider } from '../../../../context';
11
+ import { mockSCStore } from '../../../../context/mockStore';
12
+ import Routes from '../../../../utils/Route';
13
+ import { ToastBottomHelper } from '../../../../utils/Utils';
14
+ import SetupConfigCondition from '../SetupConfigCondition';
16
15
 
17
16
  jest.mock('react', () => {
18
17
  return {
@@ -214,8 +213,10 @@ describe('Test SetupConfigCondition', () => {
214
213
  });
215
214
  expect(modal.props.isVisible).toBeFalsy();
216
215
 
216
+ const modalGridItems = instance.findAllByType(GridItem);
217
+
217
218
  await act(async () => {
218
- instance.findAllByType(CheckBox)[0].props.onChange();
219
+ modalGridItems[1].findByType(TouchableWithoutFeedback).props.onPress();
219
220
  });
220
221
 
221
222
  const input = instance.findByType(TextInput);
@@ -69,6 +69,35 @@ describe('Test SetupScriptNotify', () => {
69
69
  expect(spyToast).toHaveBeenCalled();
70
70
  });
71
71
 
72
+ it('SetupScriptNotify onPress create script notify success with multi unit', async () => {
73
+ route.params.unitId = null;
74
+ route.params.multiUnit = { id: 1 };
75
+ const spyToast = jest.spyOn(ToastBottomHelper, 'success');
76
+ mock.onPost(API.AUTOMATE.ADD_SCRIPT_NOTIFY(1)).reply(200);
77
+ await act(async () => {
78
+ tree = await renderer.create(wrapComponent(route));
79
+ });
80
+ const instance = tree.root;
81
+ const inputs = instance.findAllByType(_TextInput);
82
+ expect(inputs).toHaveLength(2);
83
+
84
+ await act(async () => {
85
+ inputs[0].props.onChange('Title');
86
+ });
87
+ const touchable = instance.findAllByType(TouchableWithoutFeedback);
88
+ await act(async () => {
89
+ touchable[0].props.onPress();
90
+ });
91
+ await act(async () => {
92
+ inputs[1].props.onChange('Message');
93
+ });
94
+ const button = instance.findByType(BottomButtonView);
95
+ await act(async () => {
96
+ button.props.onPressMain();
97
+ });
98
+ expect(spyToast).toHaveBeenCalled();
99
+ });
100
+
72
101
  it('SetupScriptNotify onPress create script notify error', async () => {
73
102
  const spyToast = jest.spyOn(ToastBottomHelper, 'error');
74
103
  mock.onPost(API.AUTOMATE.ADD_SCRIPT_NOTIFY(1)).reply(400);
@@ -7,16 +7,16 @@ import Text from '../../../commons/Text';
7
7
  import { useTranslations } from '../../../hooks/Common/useTranslations';
8
8
  import Routes from '../../../utils/Route';
9
9
  import styles from './styles/AddNewAutoSmartStyles';
10
+ import { AUTOMATE_TYPE } from '../../../configs/Constants';
10
11
 
11
12
  const AddTypeSmart = ({ smartTypes, route }) => {
12
13
  const t = useTranslations();
13
14
  const { navigate, goBack } = useNavigation();
14
15
  const { automate = {}, closeScreen } = route?.params || {};
16
+ const { id, unit, type } = automate;
15
17
 
16
18
  const [selectedIndex, setSelectedIndex] = useState(
17
- automate?.id
18
- ? smartTypes.findIndex((obj) => obj.type === automate.type)
19
- : -1
19
+ id ? smartTypes.findIndex((obj) => obj.type === type) : -1
20
20
  );
21
21
 
22
22
  const handleOnContinue = useCallback(
@@ -27,18 +27,18 @@ const AddTypeSmart = ({ smartTypes, route }) => {
27
27
  ...automate,
28
28
  type: dataAutomate?.type,
29
29
  },
30
- unitId: automate?.unit,
30
+ unitId: unit,
31
31
  closeScreen: closeScreen,
32
32
  };
33
33
 
34
- if (!automate?.unit) {
34
+ if (!unit && dataAutomate.type !== AUTOMATE_TYPE.SCHEDULE) {
35
35
  navigate(Routes.SelectUnit, params);
36
36
  return;
37
37
  }
38
38
 
39
39
  navigate(dataAutomate.route, params);
40
40
  },
41
- [smartTypes, automate, closeScreen, navigate]
41
+ [smartTypes, automate, unit, closeScreen, navigate]
42
42
  );
43
43
 
44
44
  const onSelectType = (index) => {
@@ -51,7 +51,7 @@ const AddTypeSmart = ({ smartTypes, route }) => {
51
51
  <HeaderCustom isShowClose onClose={goBack} />
52
52
  <View style={styles.container}>
53
53
  <Text semibold type={'H2'} style={styles.titleCreate}>
54
- {automate?.id ? t('update_smart') : t('create_smart')}
54
+ {id ? t('update_smart') : t('create_smart')}
55
55
  </Text>
56
56
  <Text type={'Body'} style={styles.titleChoose}>
57
57
  {t('choose_the_automation_method_you_want')}
@@ -40,6 +40,7 @@ const AddActionScript = memo(
40
40
  closeScreen: currentScreenName,
41
41
  numberActionCanAdd:
42
42
  max_actions_per_automation - numberActionAdded,
43
+ routeName: unit ? null : Routes.SelectControlDevices,
43
44
  };
44
45
 
45
46
  navigate(
@@ -54,10 +55,17 @@ const AddActionScript = memo(
54
55
  image: <Notify />,
55
56
  onClick: () => {
56
57
  setIsVisible(false);
57
- navigate(Routes.SetupScriptNotify, {
58
- automate,
59
- unitId: unit,
60
- });
58
+ if (unit) {
59
+ navigate(Routes.SetupScriptNotify, {
60
+ automate,
61
+ unitId: unit,
62
+ });
63
+ } else {
64
+ navigate(Routes.SelectUnit, {
65
+ automate,
66
+ routeName: Routes.SetupScriptNotify,
67
+ });
68
+ }
61
69
  },
62
70
  },
63
71
  {
@@ -308,6 +308,8 @@ describe('Test ScriptDetail', () => {
308
308
  unitId: route.params.preAutomate.unit,
309
309
  automateId: route.params.preAutomate.id,
310
310
  numberActionCanAdd: 2,
311
+ closeScreen: undefined,
312
+ routeName: null,
311
313
  }
312
314
  );
313
315
  mockedNavigate.mockClear();
@@ -1,9 +1,9 @@
1
+ import { IconOutline } from '@ant-design/icons-react-native';
1
2
  import React from 'react';
2
- import { View, TouchableOpacity } from 'react-native';
3
- import AntDesign from 'react-native-vector-icons/AntDesign';
3
+ import { TouchableOpacity, View } from 'react-native';
4
4
  import Text from '../../../../commons/Text';
5
- import styles from '../styles/RowItemStyles';
6
5
  import { Colors } from '../../../../configs';
6
+ import styles from '../styles/RowItemStyles';
7
7
 
8
8
  const RowItem = ({ title, value, icon = null, arrow = false, onPress }) => {
9
9
  return (
@@ -17,8 +17,8 @@ const RowItem = ({ title, value, icon = null, arrow = false, onPress }) => {
17
17
  </Text>
18
18
  </View>
19
19
  <View style={[styles.itemRight, arrow && styles.center]}>
20
- {arrow && <AntDesign name="right" color={Colors.Gray7} />}
21
- {icon && <AntDesign name={icon} size={17} color={Colors.Black} />}
20
+ {arrow && <IconOutline name="right" color={Colors.Gray7} />}
21
+ {icon && <IconOutline name={icon} size={17} color={Colors.Black} />}
22
22
  </View>
23
23
  </TouchableOpacity>
24
24
  );
@@ -3,6 +3,7 @@ import moment from 'moment';
3
3
  import React, { memo, useCallback, useState } from 'react';
4
4
  import { Image, TouchableOpacity, View } from 'react-native';
5
5
 
6
+ import Notify from '../../../../assets/images/Notify.svg';
6
7
  import IconComponent from '../../../commons/IconComponent';
7
8
  import Text from '../../../commons/Text';
8
9
  import { API, Colors, SCConfig } from '../../../configs';
@@ -16,7 +17,6 @@ import { useTranslations } from '../../../hooks/Common/useTranslations';
16
17
  import { axiosPost } from '../../../utils/Apis/axios';
17
18
  import Routes from '../../../utils/Route';
18
19
  import styles from '../styles/NotificationItemStyles';
19
- import Notify from '../../../../assets/images/Notify.svg';
20
20
 
21
21
  const NotificationItem = memo(({ item }) => {
22
22
  const t = useTranslations();
@@ -72,7 +72,7 @@ const NotificationItem = memo(({ item }) => {
72
72
  },
73
73
  iconContent: (
74
74
  <IconComponent
75
- icon={'addusergroup'}
75
+ icon="usergroup-add"
76
76
  style={styles.backgroundSummer}
77
77
  />
78
78
  ),
@@ -76,7 +76,7 @@ const SelectUnit = () => {
76
76
  isAutomateTab,
77
77
  isMultiUnits,
78
78
  routeName,
79
- unit: selectedItem,
79
+ multiUnit: selectedItem,
80
80
  automateId,
81
81
  scriptName,
82
82
  oldType,
@@ -840,7 +840,7 @@ export default {
840
840
  select_unit_members: 'Select from Unit members',
841
841
  select_contacts: 'Select contacts',
842
842
  no_contact: 'No contacts',
843
- is: 'is',
843
+ is_equal: 'is equal',
844
844
  is_below: 'is below',
845
845
  is_above: 'is above',
846
846
  text_notification_content_air_quality_high:
@@ -94,8 +94,8 @@ export default {
94
94
  verify: 'Xác nhận',
95
95
  text_new_unit: 'Địa điểm',
96
96
  text_sub_units: 'Khu vực',
97
+ is_equal: 'bằng',
97
98
  is_below: 'dưới',
98
- is: 'bằng',
99
99
  is_above: 'trên',
100
100
  edit_actions_list: 'Chỉnh sửa danh sách hành động',
101
101
  des_edit_actions_list:
@@ -1320,6 +1320,8 @@ export default {
1320
1320
  smart_supplier: 'Nhà Cung Cấp Thông Minh',
1321
1321
  text_email_or_phone_number: 'Email hoặc Số điện thoại',
1322
1322
  enter_password: 'Mật khẩu',
1323
+ you_need_to_enter_password:
1324
+ 'Bạn cần nhập mật khẩu để xác nhận yêu cầu của mình',
1323
1325
  connecting_smart_account: 'Đang kết nối tài khoản',
1324
1326
  warning_connecting_smart_account:
1325
1327
  'Đừng tắt thiết bị hoặc đóng ứng dụng này trong quá trình thiết lập. Quá trình thiết lập có thể mất vài ' +