@eohjsc/react-native-smart-city 0.3.93 → 0.3.95
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.
- package/package.json +1 -1
- package/src/commons/ActionGroup/OnOffTemplate/index.js +20 -17
- package/src/commons/ActionGroup/__test__/OnOffButtonTemplate.test.js +1 -1
- package/src/commons/ActionGroup/__test__/OnOffTemplate.test.js +5 -24
- package/src/commons/SubUnit/OneTap/ItemOneTap.js +83 -85
- package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +68 -24
- package/src/commons/SubUnit/OneTap/index.js +21 -1
- package/src/configs/API.js +1 -0
- package/src/screens/ActivityLog/__test__/index.test.js +60 -2
- package/src/screens/ActivityLog/hooks/__test__/index.test.js +5 -0
- package/src/screens/ActivityLog/hooks/index.js +11 -0
- package/src/screens/AddLocationMaps/index.js +1 -1
- package/src/screens/AddNewGateway/ConnectingDevice.js +7 -0
- package/src/screens/AddNewGateway/ConnectingWifiDevice.js +6 -4
- package/src/screens/AddNewGateway/ConnectingWifiGuide.js +0 -1
- package/src/screens/AddNewGateway/RenameNewDevices.js +2 -2
- package/src/screens/AddNewGateway/ScanWifiDeviceQR.js +1 -6
- package/src/screens/AddNewGateway/SelectDeviceType.js +67 -9
- package/src/screens/AddNewGateway/ShareWifiPassword.js +20 -3
- package/src/screens/AddNewGateway/__test__/ConnectingZigbeeDevice.test.js +35 -0
- package/src/screens/AddNewGateway/__test__/ScanWifiDeviceQR.test.js +0 -10
- package/src/screens/AddNewGateway/__test__/SelectDeviceType.test.js +183 -29
- package/src/screens/AllGateway/GatewayInfo/index.js +3 -3
- package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +0 -1
- package/src/screens/Automate/MultiUnits.js +32 -18
- package/src/screens/Automate/ScriptDetail/__test__/index.test.js +36 -2
- package/src/screens/Automate/ScriptDetail/index.js +17 -3
- package/src/screens/Automate/Styles/MultiUnitsStyles.js +1 -1
- package/src/screens/Automate/__test__/MultiUnits.test.js +125 -8
- package/src/screens/Automate/__test__/index.test.js +95 -11
- package/src/screens/Automate/index.js +62 -38
- package/src/screens/Device/hooks/__test__/useEvaluateValue.test.js +24 -1
- package/src/screens/Sharing/InfoMemberUnit.js +2 -2
- package/src/screens/Sharing/MemberList.js +28 -7
- package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +32 -18
- package/src/screens/Sharing/__test__/MemberList.test.js +37 -4
- package/src/screens/SubUnit/AddSubUnit.js +1 -1
- package/src/screens/SubUnit/__test__/AddSubUnit.test.js +16 -3
- package/src/screens/Unit/AddMenu.js +42 -19
- package/src/screens/Unit/__test__/AddMenu.test.js +68 -15
- package/src/screens/Unit/components/AutomateScript/index.js +1 -1
- package/src/utils/I18n/translations/en.js +1409 -0
- package/src/utils/I18n/translations/vi.js +1414 -0
- package/src/utils/I18n/translations.ts +2 -2
- package/src/utils/Permission/backend.js +7 -0
- package/src/screens/Sharing/__test__/MemberList2.test.js +0 -74
- package/src/utils/I18n/translations/en.json +0 -1142
- package/src/utils/I18n/translations/vi.json +0 -1139
package/package.json
CHANGED
|
@@ -22,7 +22,7 @@ const getComponent = (template) => {
|
|
|
22
22
|
}
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
const OnOffTemplate = memo(({ actionGroup = {}, doAction, sensor }) => {
|
|
25
|
+
const OnOffTemplate = memo(({ actionGroup = {}, doAction, sensor = {} }) => {
|
|
26
26
|
const { configuration = {} } = actionGroup;
|
|
27
27
|
const {
|
|
28
28
|
action_on_data,
|
|
@@ -41,6 +41,7 @@ const OnOffTemplate = memo(({ actionGroup = {}, doAction, sensor }) => {
|
|
|
41
41
|
|
|
42
42
|
// eslint-disable-next-line no-unused-vars
|
|
43
43
|
const [configValues, _] = useConfigGlobalState('configValues');
|
|
44
|
+
const { device_type, is_managed_by_backend } = sensor;
|
|
44
45
|
|
|
45
46
|
const getIsOnValue = useCallback(() => {
|
|
46
47
|
const configValue = configValues[config];
|
|
@@ -65,34 +66,36 @@ const OnOffTemplate = memo(({ actionGroup = {}, doAction, sensor }) => {
|
|
|
65
66
|
return;
|
|
66
67
|
}
|
|
67
68
|
let data;
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
if (
|
|
70
|
+
allow_config_store_value &&
|
|
71
|
+
config &&
|
|
72
|
+
device_type !== DEVICE_TYPE.GOOGLE_HOME
|
|
73
|
+
) {
|
|
71
74
|
data = {
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
config_id: config,
|
|
76
|
+
config_value: isOn ? 0 : 1,
|
|
74
77
|
};
|
|
75
|
-
if (allow_config_store_value && config) {
|
|
76
|
-
data.config_id = config;
|
|
77
|
-
data.config_value = data.value;
|
|
78
|
-
}
|
|
79
78
|
}
|
|
80
|
-
|
|
79
|
+
|
|
80
|
+
if (device_type === DEVICE_TYPE.LG_THINQ) {
|
|
81
81
|
setTempIsOn((prev) => !prev);
|
|
82
82
|
}
|
|
83
83
|
await doAction(action_data, data);
|
|
84
84
|
updateStatusFromPusher(); // todo Bang read about this magic
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
if (
|
|
87
|
+
is_managed_by_backend &&
|
|
87
88
|
config &&
|
|
88
|
-
|
|
89
|
+
device_type === DEVICE_TYPE.LG_THINQ
|
|
90
|
+
) {
|
|
89
91
|
watchMultiConfigs([config]);
|
|
92
|
+
}
|
|
90
93
|
}, [
|
|
91
94
|
isOn,
|
|
92
95
|
action_off_data,
|
|
93
96
|
action_on_data,
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
device_type,
|
|
98
|
+
is_managed_by_backend,
|
|
96
99
|
allow_config_store_value,
|
|
97
100
|
config,
|
|
98
101
|
doAction,
|
|
@@ -110,10 +113,10 @@ const OnOffTemplate = memo(({ actionGroup = {}, doAction, sensor }) => {
|
|
|
110
113
|
}, [isOn]);
|
|
111
114
|
|
|
112
115
|
useEffect(() => {
|
|
113
|
-
if (
|
|
116
|
+
if (device_type !== DEVICE_TYPE.LG_THINQ) {
|
|
114
117
|
setTempIsOn(getIsOnValue());
|
|
115
118
|
}
|
|
116
|
-
}, [getIsOnValue,
|
|
119
|
+
}, [getIsOnValue, device_type]);
|
|
117
120
|
|
|
118
121
|
const Component = useMemo(() => {
|
|
119
122
|
return getComponent(actionGroup.template);
|
|
@@ -122,7 +122,7 @@ describe('Test OneBigButtonTemplate', () => {
|
|
|
122
122
|
await act(async () => {
|
|
123
123
|
await button.props.onPress();
|
|
124
124
|
});
|
|
125
|
-
expect(mockDoAction).toHaveBeenCalledWith(action_data,
|
|
125
|
+
expect(mockDoAction).toHaveBeenCalledWith(action_data, undefined);
|
|
126
126
|
};
|
|
127
127
|
|
|
128
128
|
it('action state on', async () => {
|
|
@@ -163,10 +163,7 @@ describe('Test OnOffTemplate', () => {
|
|
|
163
163
|
await act(async () => {
|
|
164
164
|
await template.props.triggerAction();
|
|
165
165
|
});
|
|
166
|
-
expect(mockDoAction).toHaveBeenCalledWith(action_off_data,
|
|
167
|
-
value: 0,
|
|
168
|
-
state: 0,
|
|
169
|
-
});
|
|
166
|
+
expect(mockDoAction).toHaveBeenCalledWith(action_off_data, undefined);
|
|
170
167
|
expect(watchMultiConfigs).toBeCalledTimes(0);
|
|
171
168
|
});
|
|
172
169
|
|
|
@@ -183,10 +180,7 @@ describe('Test OnOffTemplate', () => {
|
|
|
183
180
|
await act(async () => {
|
|
184
181
|
await template.props.triggerAction();
|
|
185
182
|
});
|
|
186
|
-
expect(mockDoAction).toHaveBeenCalledWith(action_on_data,
|
|
187
|
-
value: 1,
|
|
188
|
-
state: 1,
|
|
189
|
-
});
|
|
183
|
+
expect(mockDoAction).toHaveBeenCalledWith(action_on_data, undefined);
|
|
190
184
|
});
|
|
191
185
|
|
|
192
186
|
it('template OnOffSimpleActionTemplate doAction with is_on_value and is_managed_by_backend', async () => {
|
|
@@ -201,10 +195,7 @@ describe('Test OnOffTemplate', () => {
|
|
|
201
195
|
await act(async () => {
|
|
202
196
|
await template.props.triggerAction();
|
|
203
197
|
});
|
|
204
|
-
expect(mockDoAction).toHaveBeenCalledWith(action_off_data,
|
|
205
|
-
value: 0,
|
|
206
|
-
state: 0,
|
|
207
|
-
});
|
|
198
|
+
expect(mockDoAction).toHaveBeenCalledWith(action_off_data, undefined);
|
|
208
199
|
expect(watchMultiConfigs).toBeCalledTimes(0);
|
|
209
200
|
});
|
|
210
201
|
|
|
@@ -231,10 +222,7 @@ describe('Test OnOffTemplate', () => {
|
|
|
231
222
|
await act(async () => {
|
|
232
223
|
await template[0].props.triggerAction();
|
|
233
224
|
});
|
|
234
|
-
expect(mockDoAction).toHaveBeenCalledWith(action_data,
|
|
235
|
-
state: 0,
|
|
236
|
-
value: 0,
|
|
237
|
-
});
|
|
225
|
+
expect(mockDoAction).toHaveBeenCalledWith(action_data, undefined);
|
|
238
226
|
});
|
|
239
227
|
|
|
240
228
|
it('render with template OnOffSimpleActionTemplate with just action_data lg_thinq', async () => {
|
|
@@ -269,10 +257,7 @@ describe('Test OnOffTemplate', () => {
|
|
|
269
257
|
await act(async () => {
|
|
270
258
|
jest.runAllTimers();
|
|
271
259
|
});
|
|
272
|
-
expect(mockDoAction).toHaveBeenCalledWith(action_data,
|
|
273
|
-
value: 0,
|
|
274
|
-
state: 0,
|
|
275
|
-
});
|
|
260
|
+
expect(mockDoAction).toHaveBeenCalledWith(action_data, undefined);
|
|
276
261
|
});
|
|
277
262
|
|
|
278
263
|
it('render with template OnOffSimpleActionTemplate with action_data zigbee trigger off', async () => {
|
|
@@ -303,10 +288,8 @@ describe('Test OnOffTemplate', () => {
|
|
|
303
288
|
await template[0].props.triggerAction();
|
|
304
289
|
});
|
|
305
290
|
expect(mockDoAction).toHaveBeenCalledWith(action_off_data, {
|
|
306
|
-
state: 0,
|
|
307
291
|
config_id: 5,
|
|
308
292
|
config_value: 0,
|
|
309
|
-
value: 0,
|
|
310
293
|
});
|
|
311
294
|
});
|
|
312
295
|
|
|
@@ -338,10 +321,8 @@ describe('Test OnOffTemplate', () => {
|
|
|
338
321
|
await template[0].props.triggerAction();
|
|
339
322
|
});
|
|
340
323
|
expect(mockDoAction).toHaveBeenCalledWith(action_on_data, {
|
|
341
|
-
state: 1, // todo Bang remove this after production
|
|
342
324
|
config_id: 5,
|
|
343
325
|
config_value: 1,
|
|
344
|
-
value: 1, // this is correct
|
|
345
326
|
});
|
|
346
327
|
});
|
|
347
328
|
|
|
@@ -19,99 +19,97 @@ import { useNavigation } from '@react-navigation/native';
|
|
|
19
19
|
import Routes from '../../../utils/Route';
|
|
20
20
|
import { AccessibilityLabel, AUTOMATE_TYPE } from '../../../configs/Constants';
|
|
21
21
|
|
|
22
|
-
const ItemOneTap = memo(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const t = useTranslations();
|
|
22
|
+
const ItemOneTap = memo(({ automate = {}, wrapSyles, onPressItem }) => {
|
|
23
|
+
const { navigate } = useNavigation();
|
|
24
|
+
const { id, type, script, activate_at, author = '' } = automate;
|
|
25
|
+
const t = useTranslations();
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
const goToDetail = useCallback(() => {
|
|
28
|
+
navigate(Routes.ScriptDetail, {
|
|
29
|
+
id,
|
|
30
|
+
preAutomate: automate,
|
|
31
|
+
});
|
|
32
|
+
}, [automate, navigate, id]);
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
const handleScriptAction = useCallback(async () => {
|
|
35
|
+
const { success } = await axiosPost(API.AUTOMATE.ACTION_ONE_TAP(id));
|
|
36
|
+
if (success) {
|
|
37
|
+
ToastBottomHelper.success(t('activated_successfully'));
|
|
38
|
+
} else {
|
|
39
|
+
ToastBottomHelper.error(t('activation_failed'));
|
|
40
|
+
}
|
|
41
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
42
|
+
}, [id]);
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
44
|
+
const displayIcon = () => {
|
|
45
|
+
const iconKit = script?.icon_kit;
|
|
46
|
+
if (iconKit) {
|
|
47
|
+
return <FImage source={{ uri: iconKit }} style={styles.iconSensor} />;
|
|
48
|
+
}
|
|
49
|
+
if (type === AUTOMATE_TYPE.ONE_TAP) {
|
|
50
|
+
return <OneTap />;
|
|
51
|
+
}
|
|
52
|
+
if (type === AUTOMATE_TYPE.VALUE_CHANGE) {
|
|
53
|
+
return <ValueChange />;
|
|
54
|
+
}
|
|
55
|
+
if ([AUTOMATE_TYPE.EVENT].includes(type)) {
|
|
56
|
+
return <Event />;
|
|
57
|
+
}
|
|
58
|
+
return <Schedule />;
|
|
59
|
+
};
|
|
60
|
+
const activateAt = activate_at
|
|
61
|
+
? timeDifference(new Date(), moment(activate_at), true)
|
|
62
|
+
: null;
|
|
63
|
+
return (
|
|
64
|
+
<TouchableWithoutFeedback
|
|
65
|
+
onPress={onPressItem || goToDetail}
|
|
66
|
+
accessibilityLabel={`${AccessibilityLabel.AUTOMATE_SCRIPT_NAME}-${id}`}
|
|
67
|
+
>
|
|
68
|
+
<View style={[styles.container, wrapSyles]}>
|
|
69
|
+
<View style={styles.boxIcon}>
|
|
70
|
+
{displayIcon()}
|
|
71
|
+
{type === AUTOMATE_TYPE.ONE_TAP && (
|
|
72
|
+
<TouchableOpacity
|
|
73
|
+
accessibilityLabel={AccessibilityLabel.AUTOMATE_SCRIPT_ACTION}
|
|
74
|
+
onPress={handleScriptAction}
|
|
75
|
+
>
|
|
76
|
+
<CheckCircle />
|
|
77
|
+
</TouchableOpacity>
|
|
78
|
+
)}
|
|
79
|
+
</View>
|
|
80
|
+
<TouchableOpacity
|
|
81
|
+
accessibilityLabel={AccessibilityLabel.GO_DETAIL}
|
|
82
|
+
onPress={onPressItem || goToDetail}
|
|
83
|
+
>
|
|
84
|
+
<Text
|
|
85
|
+
numberOfLines={1}
|
|
86
|
+
semibold
|
|
87
|
+
size={14}
|
|
88
|
+
color={Colors.Gray9}
|
|
89
|
+
type="Body"
|
|
90
|
+
style={styles.name}
|
|
84
91
|
>
|
|
92
|
+
{script?.name}
|
|
93
|
+
</Text>
|
|
94
|
+
<Text numberOfLines={1} type={'Label'} color={Colors.Gray7}>
|
|
95
|
+
{t('created_by', { name: author })}
|
|
96
|
+
</Text>
|
|
97
|
+
<View style={styles.descriptionContainer}>
|
|
85
98
|
<Text
|
|
86
99
|
numberOfLines={1}
|
|
87
100
|
semibold
|
|
88
|
-
size={
|
|
89
|
-
color={Colors.
|
|
90
|
-
type="
|
|
91
|
-
style={styles.name}
|
|
101
|
+
size={12}
|
|
102
|
+
color={Colors.Gray8}
|
|
103
|
+
type="Label"
|
|
92
104
|
>
|
|
93
|
-
{
|
|
105
|
+
{activateAt && t('activated_time', { time: activateAt })}
|
|
94
106
|
</Text>
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
size={12}
|
|
103
|
-
color={Colors.Gray8}
|
|
104
|
-
type="Label"
|
|
105
|
-
>
|
|
106
|
-
{activateAt && t('activated_time', { time: activateAt })}
|
|
107
|
-
</Text>
|
|
108
|
-
<IconOutline name="right" size={12} />
|
|
109
|
-
</View>
|
|
110
|
-
</TouchableOpacity>
|
|
111
|
-
</View>
|
|
112
|
-
</TouchableWithoutFeedback>
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
);
|
|
107
|
+
<IconOutline name="right" size={12} />
|
|
108
|
+
</View>
|
|
109
|
+
</TouchableOpacity>
|
|
110
|
+
</View>
|
|
111
|
+
</TouchableWithoutFeedback>
|
|
112
|
+
);
|
|
113
|
+
});
|
|
116
114
|
|
|
117
115
|
export default ItemOneTap;
|
|
@@ -16,11 +16,13 @@ import Routes from '../../../../utils/Route';
|
|
|
16
16
|
import api from '../../../../utils/Apis/axios';
|
|
17
17
|
import { API } from '../../../../configs';
|
|
18
18
|
import { useNavigation } from '@react-navigation/native';
|
|
19
|
+
import { ToastBottomHelper } from '../../../../utils/Utils';
|
|
20
|
+
import { getTranslate } from '../../../../utils/I18n';
|
|
19
21
|
|
|
20
22
|
const mock = new MockAdapter(api.axiosInstance);
|
|
21
23
|
|
|
22
|
-
const wrapComponent = (data) => (
|
|
23
|
-
<SCProvider initState={mockSCStore(
|
|
24
|
+
const wrapComponent = (data, storeData = {}) => (
|
|
25
|
+
<SCProvider initState={mockSCStore(storeData)}>
|
|
24
26
|
<SubUnitAutomate {...data} />
|
|
25
27
|
</SCProvider>
|
|
26
28
|
);
|
|
@@ -33,33 +35,34 @@ jest.mock('react-redux', () => {
|
|
|
33
35
|
});
|
|
34
36
|
|
|
35
37
|
let tree;
|
|
36
|
-
let data
|
|
37
|
-
isOwner: true,
|
|
38
|
-
listAutomate: [
|
|
39
|
-
{
|
|
40
|
-
text: 'Scenario',
|
|
41
|
-
data: [
|
|
42
|
-
{
|
|
43
|
-
id: 1,
|
|
44
|
-
user: 6,
|
|
45
|
-
type: 'one_tap',
|
|
46
|
-
activate_at: '2021-09-17T05:30:00Z',
|
|
47
|
-
script: {
|
|
48
|
-
name: 'Joshua Ray',
|
|
49
|
-
icon: '',
|
|
50
|
-
icon_kit: '',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
type: AUTOMATE_TABS.SCENARIO,
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
};
|
|
38
|
+
let data;
|
|
58
39
|
|
|
59
40
|
describe('test Item', () => {
|
|
60
41
|
const mockedNavigate = useNavigation().navigate;
|
|
61
42
|
beforeEach(() => {
|
|
62
43
|
mockedNavigate.mockClear();
|
|
44
|
+
data = {
|
|
45
|
+
isOwner: true,
|
|
46
|
+
listAutomate: [
|
|
47
|
+
{
|
|
48
|
+
text: 'Scenario',
|
|
49
|
+
data: [
|
|
50
|
+
{
|
|
51
|
+
id: 1,
|
|
52
|
+
user: 6,
|
|
53
|
+
type: 'one_tap',
|
|
54
|
+
activate_at: '2021-09-17T05:30:00Z',
|
|
55
|
+
script: {
|
|
56
|
+
name: 'Joshua Ray',
|
|
57
|
+
icon: '',
|
|
58
|
+
icon_kit: '',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
type: AUTOMATE_TABS.SCENARIO,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
63
66
|
});
|
|
64
67
|
|
|
65
68
|
it('render SubUnitAutomate isOwner and handleOnAddNew', async () => {
|
|
@@ -189,6 +192,7 @@ describe('test Item', () => {
|
|
|
189
192
|
preAutomate: data.listAutomate[0].data[0],
|
|
190
193
|
});
|
|
191
194
|
});
|
|
195
|
+
|
|
192
196
|
it('render SubUnitAutomate script schedule and handleOnAddNew item automate', async () => {
|
|
193
197
|
data.isOwner = false;
|
|
194
198
|
data.type = 'schedule';
|
|
@@ -242,6 +246,46 @@ describe('test Item', () => {
|
|
|
242
246
|
expect(goDetail).toHaveLength(1);
|
|
243
247
|
});
|
|
244
248
|
|
|
249
|
+
it('add new automate when reach maximum', async () => {
|
|
250
|
+
data.isOwner = false;
|
|
251
|
+
data.type = 'schedule';
|
|
252
|
+
data.unit = { id: 1 };
|
|
253
|
+
|
|
254
|
+
await act(async () => {
|
|
255
|
+
tree = await create(
|
|
256
|
+
wrapComponent(data, {
|
|
257
|
+
auth: {
|
|
258
|
+
account: {
|
|
259
|
+
user: {
|
|
260
|
+
permissions: {
|
|
261
|
+
max_automations_per_unit: 0,
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const instance = tree.root;
|
|
271
|
+
const item = instance.findAll(
|
|
272
|
+
(el) =>
|
|
273
|
+
el.props.accessibilityLabel ===
|
|
274
|
+
AccessibilityLabel.SUB_UNIT_ADD_DEVICE &&
|
|
275
|
+
el.type === TouchableWithoutFeedback
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
expect(item).toHaveLength(1);
|
|
279
|
+
const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
|
|
280
|
+
await act(async () => {
|
|
281
|
+
await item[0].props.onPress();
|
|
282
|
+
});
|
|
283
|
+
expect(mockedNavigate).not.toBeCalled();
|
|
284
|
+
expect(spyToastError).toBeCalledWith(
|
|
285
|
+
getTranslate('en', 'reach_max_automations_per_unit')
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
|
|
245
289
|
it('render click select option by automation', async () => {
|
|
246
290
|
await act(async () => {
|
|
247
291
|
tree = await create(wrapComponent(data));
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
2
8
|
import { TouchableOpacity, View } from 'react-native';
|
|
3
9
|
|
|
4
10
|
import { Section } from '../..';
|
|
@@ -15,15 +21,29 @@ import {
|
|
|
15
21
|
import Text from '../../Text/index.js';
|
|
16
22
|
|
|
17
23
|
import styles from './OneTapStyles.js';
|
|
24
|
+
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
25
|
+
import { useBackendPermission } from '../../../utils/Permission/backend';
|
|
18
26
|
|
|
19
27
|
const SubUnitAutomate = ({ isOwner, listAutomate, unit, wrapItemStyle }) => {
|
|
20
28
|
const t = useTranslations();
|
|
21
29
|
const { navigate } = useNavigation();
|
|
22
30
|
const [automates, setAutomates] = useState(listAutomate[0]);
|
|
23
31
|
const [indexAutomate, setIndexAutomate] = useState(0);
|
|
32
|
+
const permissions = useBackendPermission();
|
|
24
33
|
const { name: currentScreen } = useRoute();
|
|
25
34
|
|
|
35
|
+
const totalAutomates = useMemo(
|
|
36
|
+
() => listAutomate.reduce((acc, item) => item.data.length + acc, 0),
|
|
37
|
+
[listAutomate]
|
|
38
|
+
);
|
|
39
|
+
|
|
26
40
|
const handleOnAddNew = () => {
|
|
41
|
+
if (unit?.id) {
|
|
42
|
+
if (permissions.max_automations_per_unit <= totalAutomates) {
|
|
43
|
+
ToastBottomHelper.error(t('reach_max_automations_per_unit'));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
27
47
|
switch (automates.type) {
|
|
28
48
|
case AUTOMATE_TABS.SCENARIO:
|
|
29
49
|
navigate(Routes.ScenarioName, {
|
package/src/configs/API.js
CHANGED
|
@@ -215,6 +215,7 @@ const API = {
|
|
|
215
215
|
},
|
|
216
216
|
GATEWAY: {
|
|
217
217
|
LIST: () => '/chip_manager/developer_mode_chips/',
|
|
218
|
+
COUNT: () => '/chip_manager/developer_mode_chips/count/',
|
|
218
219
|
DETAIL: (id) => `/chip_manager/developer_mode_chips/${id}/`,
|
|
219
220
|
REBOOT: (id) => `/chip_manager/developer_mode_chips/${id}/reboot_chip/`,
|
|
220
221
|
},
|
|
@@ -33,8 +33,8 @@ jest.mock('react', () => {
|
|
|
33
33
|
|
|
34
34
|
const mock = new MockAdapter(api.axiosInstance);
|
|
35
35
|
|
|
36
|
-
const wrapComponent = (route) => (
|
|
37
|
-
<SCProvider initState={mockSCStore(
|
|
36
|
+
const wrapComponent = (route, storeData = {}) => (
|
|
37
|
+
<SCProvider initState={mockSCStore(storeData)}>
|
|
38
38
|
<ActivityLog route={route} />
|
|
39
39
|
</SCProvider>
|
|
40
40
|
);
|
|
@@ -149,4 +149,62 @@ describe('Test Activity log', () => {
|
|
|
149
149
|
expect(filterPopup[0].props.isVisible).toBeTruthy();
|
|
150
150
|
expect(datePicker.props.isVisible).toBeFalsy();
|
|
151
151
|
});
|
|
152
|
+
|
|
153
|
+
const setUpActionLogScreen = () => {
|
|
154
|
+
route = {
|
|
155
|
+
params: {
|
|
156
|
+
id: 1,
|
|
157
|
+
type: 'action',
|
|
158
|
+
filterEnabled: {},
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
mock.onGet(API.DEVICE.ACTIVITY_LOG(1)).reply(200, {
|
|
163
|
+
results: [
|
|
164
|
+
{
|
|
165
|
+
id: 1,
|
|
166
|
+
content_code: 'ACTIVATED_BY',
|
|
167
|
+
params: { username: 'name' },
|
|
168
|
+
created_at: '2021-07-01T15:48:24.917932Z',
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
count: 1,
|
|
172
|
+
});
|
|
173
|
+
global.URLSearchParams = jest.fn(() => ({
|
|
174
|
+
append: jest.fn(),
|
|
175
|
+
}));
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
it('render action log has permission', async () => {
|
|
179
|
+
const storeData = {
|
|
180
|
+
auth: {
|
|
181
|
+
account: {
|
|
182
|
+
user: {
|
|
183
|
+
permissions: {
|
|
184
|
+
view_action_log: true,
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
setUpActionLogScreen();
|
|
191
|
+
|
|
192
|
+
await act(async () => {
|
|
193
|
+
tree = await create(wrapComponent(route, storeData));
|
|
194
|
+
});
|
|
195
|
+
const instance = tree.root;
|
|
196
|
+
const items = instance.findAllByType(ItemLog);
|
|
197
|
+
expect(items).toHaveLength(1);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('render action log no permission', async () => {
|
|
201
|
+
setUpActionLogScreen();
|
|
202
|
+
|
|
203
|
+
await act(async () => {
|
|
204
|
+
tree = await create(wrapComponent(route));
|
|
205
|
+
});
|
|
206
|
+
const instance = tree.root;
|
|
207
|
+
const items = instance.findAllByType(ItemLog);
|
|
208
|
+
expect(items).toHaveLength(0);
|
|
209
|
+
});
|
|
152
210
|
});
|
|
@@ -7,6 +7,11 @@ import api from '../../../../utils/Apis/axios';
|
|
|
7
7
|
import { getDataForList, getEmergencyEventDataForList } from '../../utils';
|
|
8
8
|
|
|
9
9
|
const mock = new MockAdapter(api.axiosInstance);
|
|
10
|
+
jest.mock('../../../../utils/Permission/backend', () => ({
|
|
11
|
+
useBackendPermission: jest.fn(() => ({
|
|
12
|
+
view_action_log: true,
|
|
13
|
+
})),
|
|
14
|
+
}));
|
|
10
15
|
|
|
11
16
|
describe('Test useActivityLog', () => {
|
|
12
17
|
let props;
|
|
@@ -6,6 +6,8 @@ import API from '../../../configs/API';
|
|
|
6
6
|
import t from '../../../hooks/Common/useTranslations';
|
|
7
7
|
import { AUTOMATE_TYPE } from '../../../configs/Constants';
|
|
8
8
|
import { getDataForList, getEmergencyEventDataForList } from '../utils';
|
|
9
|
+
import { useBackendPermission } from '../../../utils/Permission/backend';
|
|
10
|
+
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
9
11
|
|
|
10
12
|
const apiMaps = {
|
|
11
13
|
['action']: {
|
|
@@ -13,6 +15,7 @@ const apiMaps = {
|
|
|
13
15
|
params: (id) => ({ id: id }),
|
|
14
16
|
standardizeData: getDataForList,
|
|
15
17
|
memberUrl: (id) => API.SHARE.UNITS_MEMBERS(id),
|
|
18
|
+
needPermission: 'view_action_log',
|
|
16
19
|
},
|
|
17
20
|
['emergency_event']: {
|
|
18
21
|
url: () => API.EMERGENCY_BUTTON.ACTIVITY_LOG(),
|
|
@@ -54,7 +57,15 @@ export default ({ id, type, share, filterEnabled }) => {
|
|
|
54
57
|
});
|
|
55
58
|
const api = apiMaps[type];
|
|
56
59
|
|
|
60
|
+
const permissions = useBackendPermission();
|
|
57
61
|
const fetchData = async (filters) => {
|
|
62
|
+
if (api?.needPermission && !permissions?.[api?.needPermission]) {
|
|
63
|
+
setIsLoading(false);
|
|
64
|
+
setIsRefreshing(false);
|
|
65
|
+
ToastBottomHelper.error(t(`no_permission_${api?.needPermission}`));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
const { page } = filters;
|
|
59
70
|
setPage(page);
|
|
60
71
|
if (page === 1) {
|
|
@@ -287,7 +287,7 @@ const AddLocationMaps = memo(() => {
|
|
|
287
287
|
onPressMain={onDone}
|
|
288
288
|
secondaryTitle={t('cancel')}
|
|
289
289
|
onPressSecondary={goBack}
|
|
290
|
-
typeMain={
|
|
290
|
+
typeMain={searchedLocation?.description ? 'primaryText' : 'disabled'}
|
|
291
291
|
typeSecondary="primaryText"
|
|
292
292
|
accessibilityLabelPrefix="LOCATION_"
|
|
293
293
|
disableBackgroundMainButton
|