@eohjsc/react-native-smart-city 0.7.19 → 0.7.21

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 (32) hide show
  1. package/assets/images/Schedule.svg +39 -0
  2. package/assets/images/ValueChange.svg +49 -0
  3. package/package.json +1 -1
  4. package/src/commons/Automate/ButtonAddCondition.js +51 -0
  5. package/src/commons/Automate/ItemConditionScriptDetail.js +28 -15
  6. package/src/commons/Automate/ItemConditionScriptDetailStyles.js +9 -0
  7. package/src/configs/API.js +12 -1
  8. package/src/configs/AccessibilityLabel.js +6 -0
  9. package/src/context/actionType.ts +5 -0
  10. package/src/context/reducer.ts +30 -1
  11. package/src/navigations/UnitStack.js +8 -0
  12. package/src/screens/AddNewGateway/ScanDeviceLocal.js +15 -14
  13. package/src/screens/AddNewGateway/__test__/ScanDeviceLocal.test.js +51 -1
  14. package/src/screens/AddNewGateway/hooks/useConnectDevice.js +11 -9
  15. package/src/screens/AllGateway/DeviceInternalDetail/__test__/index.test.js +28 -0
  16. package/src/screens/AllGateway/DeviceModbusDetail/__test__/index.test.js +1 -1
  17. package/src/screens/AllGateway/__test__/index.test.js +19 -0
  18. package/src/screens/AllGateway/hooks/useGateway.js +16 -9
  19. package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +73 -30
  20. package/src/screens/Automate/AddNewAction/__test__/SetupConfigCondition.test.js +115 -10
  21. package/src/screens/Automate/AddNewAutoSmart/AddTypeSmart.js +7 -3
  22. package/src/screens/Automate/ScriptDetail/Components/DeleteCondition.js +51 -0
  23. package/src/screens/Automate/ScriptDetail/Components/ModalAddCondition.js +196 -0
  24. package/src/screens/Automate/ScriptDetail/Styles/indexStyles.js +19 -0
  25. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +441 -47
  26. package/src/screens/Automate/ScriptDetail/index.js +402 -106
  27. package/src/screens/Automate/ScriptDetail/utils.js +7 -11
  28. package/src/screens/Automate/SetSchedule/AddEditConditionSchedule.js +173 -0
  29. package/src/screens/Automate/SetSchedule/__test__/AddEditConditionSchedule.test.js +211 -0
  30. package/src/utils/I18n/translations/en.js +24 -0
  31. package/src/utils/I18n/translations/vi.js +24 -1
  32. package/src/utils/Route/index.js +1 -0
@@ -85,6 +85,25 @@ describe('Test Gateway screen', () => {
85
85
  expect(flatList.props.data).toEqual([{ id: 1, name: 'device 1' }]);
86
86
  });
87
87
 
88
+ it('render Gateway onLoadMore', async () => {
89
+ await act(async () => {
90
+ tree = await create(wrapComponent());
91
+ });
92
+ const instance = tree.root;
93
+ const flatList = instance.findByType(FlatList);
94
+ expect(flatList.props.data).toEqual([]);
95
+ mock
96
+ .onGet(API.DEV_MODE.GATEWAY.LIST())
97
+ .reply(200, { results: [{ id: 1, name: 'device 1' }] });
98
+ await act(async () => {
99
+ await flatList.props.onMomentumScrollBegin();
100
+ });
101
+ await act(async () => {
102
+ await flatList.props.onEndReached();
103
+ });
104
+ expect(flatList.props.data).toEqual([{ id: 1, name: 'device 1' }]);
105
+ });
106
+
88
107
  it('Test render empty', async () => {
89
108
  await act(async () => {
90
109
  tree = await create(wrapComponent());
@@ -146,26 +146,29 @@ export const useGateway = () => {
146
146
  isModbus,
147
147
  numberGoBack = 1
148
148
  ) => {
149
- let success;
149
+ let response;
150
150
  if (isInternal) {
151
- success = await axiosDelete(
151
+ response = await axiosDelete(
152
152
  API.DEV_MODE.ARDUINO.DEVICE_DETAIL(gatewayId, deviceId)
153
153
  );
154
- success && setGatewayDevices((prev) => ({ ...prev, internal: [] }));
154
+ response.success &&
155
+ setGatewayDevices((prev) => ({ ...prev, internal: [] }));
155
156
  }
156
157
  if (isZigbee) {
157
- success = await axiosDelete(
158
+ response = await axiosDelete(
158
159
  API.DEV_MODE.ZIGBEE.DEVICE_DETAIL(gatewayId, deviceId)
159
160
  );
160
- success && setGatewayDevices((prev) => ({ ...prev, zigbee: [] }));
161
+ response.success &&
162
+ setGatewayDevices((prev) => ({ ...prev, zigbee: [] }));
161
163
  }
162
164
  if (isModbus) {
163
- success = await axiosDelete(
165
+ response = await axiosDelete(
164
166
  API.DEV_MODE.MODBUS.DEVICE_DETAIL(gatewayId, deviceId)
165
167
  );
166
- success && setGatewayDevices((prev) => ({ ...prev, modbus: [] }));
168
+ response.success &&
169
+ setGatewayDevices((prev) => ({ ...prev, modbus: [] }));
167
170
  }
168
- if (success) {
171
+ if (response.success) {
169
172
  ToastBottomHelper.success(t('delete_successfully'));
170
173
  navigation.pop(numberGoBack);
171
174
  }
@@ -259,7 +262,11 @@ export const useGateway = () => {
259
262
  params: { secret },
260
263
  }
261
264
  );
262
- success && setDetailChipQr(data);
265
+ if (success) {
266
+ setDetailChipQr(data);
267
+ } else {
268
+ ToastBottomHelper.error(t('chip_qr_not_found'));
269
+ }
263
270
  }, []);
264
271
 
265
272
  return {
@@ -16,6 +16,10 @@ import Routes from '../../../utils/Route';
16
16
  import { ToastBottomHelper } from '../../../utils/Utils';
17
17
  import NewActionWrapper from './NewActionWrapper';
18
18
  import styles from './Styles/SetupSensorStyles';
19
+ import { useSCContextSelector } from '../../../context';
20
+ import { axiosPost, axiosPut } from '../../../utils/Apis/axios';
21
+ import { API } from '../../../configs';
22
+ import { AccessibilityLabel, AUTOMATE_TYPE } from '../../../configs/Constants';
19
23
 
20
24
  const valueEvaluationToOptions = (valueEvaluation) => {
21
25
  if (valueEvaluation.template === 'range') {
@@ -51,22 +55,23 @@ const SetupConfigCondition = () => {
51
55
  const [selectedCondition, setSelectedCondition] = useState(undefined);
52
56
  const [customCondition, setCustomCondition] = useState(undefined);
53
57
  const [isShowModal, setIsShowModal] = useState(false);
54
- const normalConditions = useMemo(() => {
55
- return [
56
- {
57
- label: `${t('is_below')} (<)`,
58
- value: '<',
59
- },
60
- {
61
- label: `${t('is_equal')} (=)`,
62
- value: '=',
63
- },
64
- {
65
- label: `${t('is_above')} (>)`,
66
- value: '>',
67
- },
68
- ];
69
- }, [t]);
58
+
59
+ const isCreateCondition = useSCContextSelector(
60
+ (state) => state.automate.isCreateCondition
61
+ );
62
+ const isUpdateCondition = useSCContextSelector(
63
+ (state) => state.automate.isUpdateCondition
64
+ );
65
+
66
+ const normalConditions = useMemo(
67
+ () => [
68
+ { label: `${t('is_below')} (<)`, value: '<' },
69
+ { label: `${t('is_equal')} (=)`, value: '=' },
70
+ { label: `${t('is_above')} (>)`, value: '>' },
71
+ ],
72
+ [t]
73
+ );
74
+
70
75
  const conditionOptions = useMemo(() => {
71
76
  if (config?.value_evaluations?.length) {
72
77
  const options = [];
@@ -85,15 +90,56 @@ const SetupConfigCondition = () => {
85
90
  setIsShowModal(true);
86
91
  };
87
92
 
88
- const setupCondition = (condition) => {
93
+ const handleNavigate = useCallback(async () => {
94
+ if (!isCreateCondition && !isUpdateCondition) {
95
+ navigate(Routes.ValueChangeName, {
96
+ automate,
97
+ closeScreen,
98
+ unitId,
99
+ });
100
+ return;
101
+ }
102
+ const data = {
103
+ type: AUTOMATE_TYPE.VALUE_CHANGE,
104
+ condition: automate.condition,
105
+ config: automate.config,
106
+ value: automate.value,
107
+ };
108
+ const apiCall = isCreateCondition
109
+ ? await axiosPost(API.AUTOMATE.ADD_CONDITION(automate.id), data)
110
+ : await axiosPut(
111
+ API.AUTOMATE.UPDATE_CONDITION(automate.id, isUpdateCondition),
112
+ data
113
+ );
114
+
115
+ const { success } = apiCall;
116
+
117
+ if (success) {
118
+ ToastBottomHelper.success(
119
+ isCreateCondition
120
+ ? t('add_condition_success')
121
+ : t('edit_condition_success')
122
+ );
123
+ navigate({
124
+ name: Routes.ScriptDetail,
125
+ params: { preAutomate: automate },
126
+ });
127
+ }
128
+ }, [
129
+ automate,
130
+ closeScreen,
131
+ isCreateCondition,
132
+ isUpdateCondition,
133
+ navigate,
134
+ t,
135
+ unitId,
136
+ ]);
137
+
138
+ const setupCondition = async (condition) => {
89
139
  automate.condition = condition?.condition;
90
140
  automate.value = condition?.value;
91
141
  setSelectedCondition(condition);
92
- navigate(Routes.ValueChangeName, {
93
- automate,
94
- closeScreen,
95
- unitId,
96
- });
142
+ handleNavigate();
97
143
  };
98
144
 
99
145
  const resetCustomizeCondition = useCallback(() => {
@@ -146,9 +192,10 @@ const SetupConfigCondition = () => {
146
192
  {t('when_value_is', { config_name: config.name })}
147
193
  </Text>
148
194
  <View style={styles.flatListCondition}>
149
- {normalConditions.map((item) => {
195
+ {normalConditions.map((item, index) => {
150
196
  return (
151
197
  <GridItem
198
+ key={`GridItem${index}`}
152
199
  onPress={() => {
153
200
  automate.condition = item.value;
154
201
  setCustomCondition(item.value);
@@ -180,18 +227,14 @@ const SetupConfigCondition = () => {
180
227
  <Button
181
228
  type="primary"
182
229
  title={t('continue')}
183
- testID={'continue_button'}
184
- onPress={() => {
230
+ accessibilityLabel={AccessibilityLabel.BUTTON_CONTINUE}
231
+ onPress={async () => {
185
232
  if (isNaN(automate.value) || !automate.condition) {
186
233
  ToastBottomHelper.error(t('please_enter_a_number'), '', 3000);
187
234
  return;
188
235
  }
189
236
  setIsShowModal(false);
190
- navigate(Routes.ValueChangeName, {
191
- automate,
192
- closeScreen,
193
- unitId,
194
- });
237
+ handleNavigate();
195
238
  }}
196
239
  style={styles.buttonContinue}
197
240
  textSemiBold={false}
@@ -7,18 +7,15 @@ import { showAllGridItems } from '../../../../commons/ActionTemplate/__test__/ut
7
7
  import TextInput from '../../../../commons/Form/TextInput';
8
8
  import GridItem from '../../../../commons/Grid/GridItem';
9
9
  import { ModalCustom } from '../../../../commons/Modal';
10
- import { SCProvider } from '../../../../context';
10
+ import { SCProvider, useSCContextSelector } from '../../../../context';
11
11
  import { mockSCStore } from '../../../../context/mockStore';
12
12
  import Routes from '../../../../utils/Route';
13
13
  import { ToastBottomHelper } from '../../../../utils/Utils';
14
14
  import SetupConfigCondition from '../SetupConfigCondition';
15
-
16
- jest.mock('react', () => {
17
- return {
18
- ...jest.requireActual('react'),
19
- memo: (x) => x,
20
- };
21
- });
15
+ import AccessibilityLabel from '../../../../configs/AccessibilityLabel';
16
+ import { API } from '../../../../configs';
17
+ import api from '../../../../utils/Apis/axios';
18
+ import MockAdapter from 'axios-mock-adapter';
22
19
 
23
20
  const BOOLEAN_VALUE_EVALUATION_CONFIGURATION = {
24
21
  id: 1,
@@ -39,10 +36,25 @@ const wrapComponent = (configuration, onPress) => (
39
36
  </SCProvider>
40
37
  );
41
38
 
39
+ jest.mock('../../../../context', () => ({
40
+ ...jest.requireActual('../../../../context'),
41
+ useSCContextSelector: jest.fn(),
42
+ }));
43
+
44
+ const mock = new MockAdapter(api.axiosInstance);
45
+
42
46
  describe('Test SetupConfigCondition', () => {
43
47
  const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
44
48
 
45
49
  let tree;
50
+ useSCContextSelector.mockImplementation((selector) =>
51
+ selector({
52
+ automate: {
53
+ isCreateCondition: false,
54
+ isUpdateCondition: false,
55
+ },
56
+ })
57
+ );
46
58
  beforeEach(() => {
47
59
  tree = null;
48
60
  spyToastError.mockClear();
@@ -150,6 +162,14 @@ describe('Test SetupConfigCondition', () => {
150
162
  });
151
163
 
152
164
  it('choose config value evaluation', async () => {
165
+ useSCContextSelector.mockImplementation((selector) =>
166
+ selector({
167
+ automate: {
168
+ isCreateCondition: false,
169
+ isUpdateCondition: false,
170
+ },
171
+ })
172
+ );
153
173
  useRoute.mockReturnValue({
154
174
  params: {
155
175
  automate: {},
@@ -183,6 +203,14 @@ describe('Test SetupConfigCondition', () => {
183
203
  });
184
204
 
185
205
  const testCustomCondition = async (value) => {
206
+ useSCContextSelector.mockImplementation((selector) =>
207
+ selector({
208
+ automate: {
209
+ isCreateCondition: false,
210
+ isUpdateCondition: false,
211
+ },
212
+ })
213
+ );
186
214
  useRoute.mockReturnValue({
187
215
  params: {
188
216
  automate: {},
@@ -225,7 +253,7 @@ describe('Test SetupConfigCondition', () => {
225
253
  });
226
254
 
227
255
  const bottomButtonView = instance.findByProps({
228
- testID: 'continue_button',
256
+ accessibilityLabel: AccessibilityLabel.BUTTON_CONTINUE,
229
257
  });
230
258
 
231
259
  await act(async () => {
@@ -273,7 +301,7 @@ describe('Test SetupConfigCondition', () => {
273
301
  });
274
302
 
275
303
  const bottomButtonView = instance.findByProps({
276
- testID: 'continue_button',
304
+ accessibilityLabel: AccessibilityLabel.BUTTON_CONTINUE,
277
305
  });
278
306
 
279
307
  await act(async () => {
@@ -333,4 +361,81 @@ describe('Test SetupConfigCondition', () => {
333
361
  'Customize conditions'
334
362
  );
335
363
  });
364
+
365
+ it('test isCreateCondition success', async () => {
366
+ useSCContextSelector.mockImplementation((selector) =>
367
+ selector({
368
+ automate: {
369
+ isCreateCondition: true,
370
+ isUpdateCondition: false,
371
+ },
372
+ })
373
+ );
374
+ useRoute.mockReturnValue({
375
+ params: {
376
+ config: {
377
+ id: 1,
378
+ name: 'config name',
379
+ title: 'is below (<)',
380
+ value_evaluations: [BOOLEAN_VALUE_EVALUATION_CONFIGURATION],
381
+ },
382
+ automate: { id: 1 },
383
+ sensorData: [],
384
+ },
385
+ });
386
+ mock.onPost(API.AUTOMATE.ADD_CONDITION(1)).reply(200);
387
+ await act(async () => {
388
+ tree = await create(wrapComponent());
389
+ });
390
+ const instance = tree.root;
391
+
392
+ const gridItems = instance.findAllByType(GridItem);
393
+ expect(gridItems).toHaveLength(3);
394
+ await act(async () => {
395
+ gridItems[0].props.onPress();
396
+ });
397
+
398
+ expect(global.mockedNavigate).toBeCalledWith({
399
+ name: 'ScriptDetail',
400
+ params: {
401
+ preAutomate: { condition: 'value_evaluation', id: 1, value: [1, 0] },
402
+ },
403
+ });
404
+ });
405
+
406
+ it('test isUpdateCondition fail', async () => {
407
+ useSCContextSelector.mockImplementation((selector) =>
408
+ selector({
409
+ automate: {
410
+ isCreateCondition: false,
411
+ isUpdateCondition: 1,
412
+ },
413
+ })
414
+ );
415
+ useRoute.mockReturnValue({
416
+ params: {
417
+ config: {
418
+ id: 1,
419
+ name: 'config name',
420
+ title: 'is below (<)',
421
+ value_evaluations: [BOOLEAN_VALUE_EVALUATION_CONFIGURATION],
422
+ },
423
+ automate: { id: 1 },
424
+ sensorData: [],
425
+ },
426
+ });
427
+ mock.onPut(API.AUTOMATE.UPDATE_CONDITION(1, 1)).reply(400);
428
+ await act(async () => {
429
+ tree = await create(wrapComponent());
430
+ });
431
+ const instance = tree.root;
432
+
433
+ const gridItems = instance.findAllByType(GridItem);
434
+ expect(gridItems).toHaveLength(3);
435
+ await act(async () => {
436
+ gridItems[0].props.onPress();
437
+ });
438
+
439
+ expect(global.mockedNavigate).not.toBeCalled();
440
+ });
336
441
  });
@@ -1,5 +1,5 @@
1
1
  import { useNavigation } from '@react-navigation/native';
2
- import React, { useCallback, useState } from 'react';
2
+ import React, { useCallback, useContext, useState } from 'react';
3
3
  import { View } from 'react-native';
4
4
  import ItemAutomate from '../../../commons/Automate/ItemAutomate';
5
5
  import { HeaderCustom } from '../../../commons/Header';
@@ -8,9 +8,12 @@ import { useTranslations } from '../../../hooks/Common/useTranslations';
8
8
  import Routes from '../../../utils/Route';
9
9
  import styles from './styles/AddNewAutoSmartStyles';
10
10
  import { AUTOMATE_TYPE } from '../../../configs/Constants';
11
+ import { SCContext } from '../../../context';
12
+ import { Action } from '../../../context/actionType';
11
13
 
12
14
  const AddTypeSmart = ({ smartTypes, route }) => {
13
15
  const t = useTranslations();
16
+ const { setAction } = useContext(SCContext);
14
17
  const { navigate, goBack } = useNavigation();
15
18
  const { automate = {}, closeScreen } = route?.params || {};
16
19
  const { id, unit, type } = automate;
@@ -30,7 +33,8 @@ const AddTypeSmart = ({ smartTypes, route }) => {
30
33
  unitId: unit,
31
34
  closeScreen: closeScreen,
32
35
  };
33
-
36
+ setAction(Action.IS_CREATE_CONDITION, false);
37
+ setAction(Action.IS_UPDATE_CONDITION, false);
34
38
  if (!unit && dataAutomate.type === AUTOMATE_TYPE.VALUE_CHANGE) {
35
39
  navigate(Routes.SelectUnit, params);
36
40
  return;
@@ -38,7 +42,7 @@ const AddTypeSmart = ({ smartTypes, route }) => {
38
42
 
39
43
  navigate(dataAutomate.route, params);
40
44
  },
41
- [smartTypes, automate, unit, closeScreen, navigate]
45
+ [smartTypes, automate, unit, closeScreen, setAction, navigate]
42
46
  );
43
47
 
44
48
  const onSelectType = (index) => {
@@ -0,0 +1,51 @@
1
+ import React, { useCallback } from 'react';
2
+ import { API, Colors } from '../../../../configs';
3
+ import AlertAction from '../../../../commons/AlertAction';
4
+ import { axiosDelete } from '../../../../utils/Apis/axios';
5
+ import { ToastBottomHelper } from '../../../../utils/Utils';
6
+ import { useTranslations } from '../../../../hooks/Common/useTranslations';
7
+ import AccessibilityLabel from '../../../../configs/AccessibilityLabel';
8
+
9
+ const DeleteCondition = ({
10
+ automate,
11
+ setAutomate,
12
+ condition,
13
+ isVisible,
14
+ setIsVisible,
15
+ }) => {
16
+ const t = useTranslations();
17
+ const deleteScript = useCallback(async () => {
18
+ const { success } = await axiosDelete(
19
+ API.AUTOMATE.DELETE_CONDITION(automate.id, condition.id)
20
+ );
21
+ if (success) {
22
+ ToastBottomHelper.success(t('removed_successfully'));
23
+ const conditions = automate.conditions.filter(
24
+ (el) => el.id !== condition.id
25
+ );
26
+ setAutomate((prev) => ({ ...prev, conditions }));
27
+ } else {
28
+ ToastBottomHelper.error(t('remove_failed'));
29
+ }
30
+ setIsVisible({});
31
+ }, [automate, condition.id, setAutomate, setIsVisible, t]);
32
+
33
+ const hidePopup = () => setIsVisible({});
34
+
35
+ return (
36
+ <AlertAction
37
+ visible={isVisible}
38
+ hideModal={hidePopup}
39
+ title={t('title_delete_condition')}
40
+ message={t('message_delete_condition')}
41
+ leftButtonTitle={t('cancel')}
42
+ leftButtonClick={hidePopup}
43
+ rightButtonTitle={t('remove')}
44
+ rightButtonClick={deleteScript}
45
+ rightButtonStyle={{ color: Colors.Red6 }}
46
+ accessibilityLabel={AccessibilityLabel.AUTOMATE_DELETE_CONDITION}
47
+ />
48
+ );
49
+ };
50
+
51
+ export default DeleteCondition;
@@ -0,0 +1,196 @@
1
+ import React, { memo, useContext, useMemo } from 'react';
2
+
3
+ import { useRoute } from '@react-navigation/native';
4
+
5
+ import { ModalCustom } from '../../../../commons/Modal';
6
+ import { StyleSheet, View } from 'react-native';
7
+ import { Colors } from '../../../../configs';
8
+ import { useTranslations } from '../../../../hooks/Common/useTranslations';
9
+ import { useBackendPermission } from '../../../../utils/Permission/backend';
10
+ import ValueChange from '../../../../../assets/images/Automation-fill.svg';
11
+ import Schedule from '../../../../../assets/images/Schedule.svg';
12
+ import { TouchableOpacity } from 'react-native';
13
+ import { Text } from '../../../../commons';
14
+ import Routes from '../../../../utils/Route';
15
+ import AccessibilityLabel from '../../../../configs/AccessibilityLabel';
16
+ import { SCContext } from '../../../../context';
17
+ import { Action } from '../../../../context/actionType';
18
+
19
+ const ModalAddCondition = memo(
20
+ ({ automate, isVisible, setIsVisible, numberActionAdded, navigate }) => {
21
+ const t = useTranslations();
22
+ const { setAction } = useContext(SCContext);
23
+ const { unit } = automate;
24
+ const { name: currentScreenName } = useRoute();
25
+ const permissions = useBackendPermission();
26
+ const { max_actions_per_automation } = permissions || {};
27
+
28
+ const listItem = useMemo(() => {
29
+ return [
30
+ {
31
+ id: 'time_frame',
32
+ text: t('time_frame'),
33
+ example: t('ex_time_frame'),
34
+ image: <Schedule />,
35
+ onClick: () => {
36
+ setIsVisible(false);
37
+ navigate(Routes.AddEditConditionSchedule, {
38
+ automate: automate,
39
+ closeScreen: Routes.ScriptDetail,
40
+ });
41
+ },
42
+ },
43
+ {
44
+ id: 'when_value_change',
45
+ text: t('when_value_change'),
46
+ example: t('ex_when_value_change'),
47
+ image: <ValueChange />,
48
+ onClick: () => {
49
+ setIsVisible(false);
50
+ const navParams = {
51
+ automate: automate,
52
+ closeScreen: currentScreenName,
53
+ numberActionCanAdd:
54
+ max_actions_per_automation - numberActionAdded,
55
+ routeName: unit ? null : Routes.SelectControlDevices,
56
+ };
57
+ setAction(Action.IS_UPDATE_CONDITION, false);
58
+ setAction(Action.IS_CREATE_CONDITION, true);
59
+ navigate(Routes.SelectMonitorDevices, navParams);
60
+ if (unit) {
61
+ navigate(Routes.SelectMonitorDevices, navParams);
62
+ } else {
63
+ navigate(Routes.SelectUnit, {
64
+ automate,
65
+ routeName: Routes.SelectMonitorDevices,
66
+ });
67
+ }
68
+ },
69
+ },
70
+ ];
71
+ }, [
72
+ automate,
73
+ currentScreenName,
74
+ max_actions_per_automation,
75
+ navigate,
76
+ numberActionAdded,
77
+ setAction,
78
+ setIsVisible,
79
+ t,
80
+ unit,
81
+ ]);
82
+
83
+ const hidePopup = () => setIsVisible(false);
84
+
85
+ return (
86
+ <ModalCustom
87
+ isVisible={isVisible}
88
+ onBackButtonPress={hidePopup}
89
+ onBackdropPress={hidePopup}
90
+ style={styles.container}
91
+ >
92
+ <View style={styles.popoverStyle}>
93
+ <View style={styles.modalWrapper}>
94
+ <View style={styles.modalHeader}>
95
+ <Text style={styles.modalHeaderText} semibold>
96
+ {t('add_condition')}
97
+ </Text>
98
+ </View>
99
+ {listItem.map((item, index) => {
100
+ return (
101
+ <View style={styles.rowItem} key={`rowItem-${index}`}>
102
+ <TouchableOpacity
103
+ onPress={item.onClick}
104
+ accessibilityLabel={
105
+ AccessibilityLabel.AUTOMATE_LIST_SCRIPT_ACTION
106
+ }
107
+ >
108
+ <View style={styles.wapItem}>
109
+ <View style={styles.imageItem}>{item.image}</View>
110
+ <View style={styles.colItem}>
111
+ <Text type="Body" semibold>
112
+ {item.text}
113
+ </Text>
114
+ <Text style={styles.textItem} numberOfLines={1}>
115
+ {item.example}
116
+ </Text>
117
+ </View>
118
+ </View>
119
+ </TouchableOpacity>
120
+ </View>
121
+ );
122
+ })}
123
+ </View>
124
+ </View>
125
+ </ModalCustom>
126
+ );
127
+ }
128
+ );
129
+
130
+ export default ModalAddCondition;
131
+
132
+ const styles = StyleSheet.create({
133
+ container: {
134
+ flex: 1,
135
+ margin: 0,
136
+ },
137
+ popoverStyle: {
138
+ width: '100%',
139
+ backgroundColor: Colors.White,
140
+ bottom: 0,
141
+ left: 0,
142
+ position: 'absolute',
143
+ borderRadius: 10,
144
+ paddingBottom: 50,
145
+ },
146
+ modalWrapper: {
147
+ flex: 1,
148
+ flexDirection: 'column',
149
+ backgroundColor: Colors.White,
150
+ borderRadius: 10,
151
+ },
152
+ modalHeader: {
153
+ padding: 16,
154
+ marginBottom: 10,
155
+ backgroundColor: Colors.White,
156
+ alignItems: 'center',
157
+ justifyContent: 'center',
158
+ borderBottomWidth: 1,
159
+ borderTopLeftRadius: 20,
160
+ borderTopRightRadius: 20,
161
+ borderColor: Colors.Gray4,
162
+ },
163
+ modalHeaderText: {
164
+ fontSize: 16,
165
+ lineHeight: 24,
166
+ color: Colors.Gray9,
167
+ },
168
+ rowItem: {
169
+ padding: 16,
170
+ marginBottom: 10,
171
+ marginHorizontal: 10,
172
+ backgroundColor: Colors.White,
173
+ borderRadius: 16,
174
+ borderWidth: 1,
175
+ borderColor: Colors.Gray4,
176
+ },
177
+ wapItem: {
178
+ flexDirection: 'row',
179
+ alignItems: 'center',
180
+ justifyContent: 'center',
181
+ },
182
+ imageItem: {
183
+ width: '10%',
184
+ alignItems: 'center',
185
+ justifyContent: 'center',
186
+ marginRight: 20,
187
+ },
188
+ colItem: {
189
+ flex: 1,
190
+ flexDirection: 'column',
191
+ },
192
+ textItem: {
193
+ color: Colors.Gray8,
194
+ fontSize: 12,
195
+ },
196
+ });