@eohjsc/react-native-smart-city 0.2.82 → 0.2.83
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 +3 -3
- package/src/commons/ActionGroup/CurtainButtonTemplate.js +32 -21
- package/src/commons/ActionGroup/NumberUpDownActionTemplate.js +8 -6
- package/src/commons/ActionGroup/OnOffTemplate/index.js +11 -3
- package/src/commons/ActionGroup/OneBigButtonTemplate.js +10 -7
- package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +5 -2
- package/src/commons/ActionGroup/StatesGridActionTemplate.js +7 -3
- package/src/commons/ActionGroup/ThreeButtonTemplate.js +33 -24
- package/src/commons/ActionGroup/__test__/OnOffTemplate.test.js +18 -6
- package/src/commons/ActionGroup/__test__/OneBigButtonTemplate.test.js +9 -1
- package/src/commons/ActionGroup/__test__/OptionsDropdownTemplate.test.js +25 -13
- package/src/commons/ActionGroup/__test__/index.test.js +48 -14
- package/src/commons/Device/HorizontalBarChart.js +7 -1
- package/src/commons/Device/ItemDevice.js +8 -5
- package/src/commons/EmergencyButton/AlertSendConfirm.js +2 -2
- package/src/commons/EmergencyButton/AlertSent.js +2 -2
- package/src/commons/SubUnit/Favorites/index.js +2 -0
- package/src/commons/SubUnit/ShortDetail.js +7 -1
- package/src/configs/API.js +2 -4
- package/src/configs/Constants.js +2 -0
- package/src/iot/RemoteControl/Internet.js +8 -1
- package/src/iot/RemoteControl/index.js +4 -2
- package/src/screens/Device/EditDevice/__test__/EditDevice.test.js +2 -2
- package/src/screens/Device/EditDevice/index.js +2 -2
- package/src/screens/Device/__test__/detail.test.js +18 -11
- package/src/screens/Device/components/SensorConnectStatusViewHeader.js +2 -2
- package/src/screens/Device/components/SensorDisplayItem.js +2 -2
- package/src/screens/Device/detail.js +58 -20
- package/src/screens/Notification/__test__/NotificationItem.test.js +186 -14
- package/src/screens/Notification/components/NotificationItem.js +128 -2
- package/src/screens/Notification/styles/NotificationItemStyles.js +3 -3
- package/src/screens/SubUnit/AddSubUnit.js +4 -1
- package/src/screens/SubUnit/__test__/AddSubUnit.test.js +148 -0
- package/src/screens/Unit/Detail.js +10 -0
- package/src/screens/Unit/Summaries.js +2 -2
- package/src/screens/Unit/__test__/CheckSendEmail.test.js +10 -0
- package/src/screens/Unit/__test__/Detail.test.js +10 -0
- package/src/screens/Unit/components/__test__/SharedUnit.test.js +21 -2
- package/src/screens/UnitSummary/__test__/index.test.js +3 -3
- package/src/screens/UnitSummary/components/RunningDevices/__test__/index.test.js +2 -2
- package/src/screens/UnitSummary/index.js +52 -9
- package/src/utils/I18n/translations/en.json +7 -0
- package/src/utils/I18n/translations/vi.json +7 -0
|
@@ -199,4 +199,152 @@ describe('Test AddSubUnit', () => {
|
|
|
199
199
|
visibilityTime: 1000,
|
|
200
200
|
});
|
|
201
201
|
});
|
|
202
|
+
|
|
203
|
+
test('test create Unit', async () => {
|
|
204
|
+
const response = {
|
|
205
|
+
success: true,
|
|
206
|
+
status: 200,
|
|
207
|
+
data: {
|
|
208
|
+
id: 2,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
route.params = {
|
|
212
|
+
...route.params,
|
|
213
|
+
location: 'Unit address',
|
|
214
|
+
isAddUnit: true,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
axios.post.mockImplementation(async () => {
|
|
218
|
+
return response;
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
await act(async () => {
|
|
222
|
+
tree = await create(wrapComponent(route));
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const instance = tree.root;
|
|
226
|
+
const viewButtonBottom = await makeValidateData(instance);
|
|
227
|
+
|
|
228
|
+
await act(async () => {
|
|
229
|
+
await viewButtonBottom.props.onRightClick();
|
|
230
|
+
});
|
|
231
|
+
expect(axios.post).toHaveBeenCalled();
|
|
232
|
+
expect(mockedNavigate).toHaveBeenCalledWith(Routes.UnitStack, {
|
|
233
|
+
screen: Routes.UnitDetail,
|
|
234
|
+
params: {
|
|
235
|
+
unitId: response.data.id,
|
|
236
|
+
routeName: Routes.DashboardStack,
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('test choose Location', async () => {
|
|
242
|
+
route.params = {
|
|
243
|
+
...route.params,
|
|
244
|
+
location: '',
|
|
245
|
+
isAddUnit: true,
|
|
246
|
+
};
|
|
247
|
+
await act(async () => {
|
|
248
|
+
tree = await create(wrapComponent(route));
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const instance = tree.root;
|
|
252
|
+
const buttonChooseLocation = instance.find(
|
|
253
|
+
(el) => el.props.testID === TESTID.ADD_SUB_UNIT_BUTTON_CHOOSE_LOCATION
|
|
254
|
+
);
|
|
255
|
+
act(() => {
|
|
256
|
+
buttonChooseLocation.props.onPress();
|
|
257
|
+
});
|
|
258
|
+
expect(mockedNavigate).toHaveBeenCalledWith(Routes.AddLocationMaps);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test('test create Unit Fail', async () => {
|
|
262
|
+
const response = {
|
|
263
|
+
success: false,
|
|
264
|
+
status: 400,
|
|
265
|
+
};
|
|
266
|
+
route.params = {
|
|
267
|
+
...route.params,
|
|
268
|
+
location: 'Unit address',
|
|
269
|
+
isAddUnit: true,
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
axios.post.mockImplementation(async () => {
|
|
273
|
+
return response;
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
await act(async () => {
|
|
277
|
+
tree = await create(wrapComponent(route));
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const instance = tree.root;
|
|
281
|
+
const viewButtonBottom = await makeValidateData(instance);
|
|
282
|
+
|
|
283
|
+
await act(async () => {
|
|
284
|
+
await viewButtonBottom.props.onRightClick();
|
|
285
|
+
});
|
|
286
|
+
expect(axios.post).toHaveBeenCalled();
|
|
287
|
+
expect(Toast.show).toHaveBeenCalledWith({
|
|
288
|
+
type: 'error',
|
|
289
|
+
position: 'bottom',
|
|
290
|
+
text1: getTranslate('en', 'text_create_unit_fail'),
|
|
291
|
+
visibilityTime: 1000,
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('test create sub-unit type AddHassiDevice', async () => {
|
|
296
|
+
const response = {
|
|
297
|
+
status: 200,
|
|
298
|
+
success: true,
|
|
299
|
+
data: {},
|
|
300
|
+
};
|
|
301
|
+
route.params = {
|
|
302
|
+
...route.params,
|
|
303
|
+
addType: 'AddHassiDevice',
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
axios.post.mockImplementation(async () => {
|
|
307
|
+
return response;
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
await act(async () => {
|
|
311
|
+
tree = await create(wrapComponent(route));
|
|
312
|
+
});
|
|
313
|
+
const instance = tree.root;
|
|
314
|
+
const viewButtonBottom = await makeValidateData(instance);
|
|
315
|
+
await act(async () => {
|
|
316
|
+
await viewButtonBottom.props.onRightClick();
|
|
317
|
+
});
|
|
318
|
+
expect(axios.post).toHaveBeenCalled();
|
|
319
|
+
expect(mockedGoBack).toHaveBeenCalled();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('test create sub-unit type AddNewGateway', async () => {
|
|
323
|
+
const response = {
|
|
324
|
+
status: 200,
|
|
325
|
+
success: true,
|
|
326
|
+
data: {},
|
|
327
|
+
};
|
|
328
|
+
route.params = {
|
|
329
|
+
...route.params,
|
|
330
|
+
addType: 'AddNewGateway',
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
axios.post.mockImplementation(async () => {
|
|
334
|
+
return response;
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
await act(async () => {
|
|
338
|
+
tree = await create(wrapComponent(route));
|
|
339
|
+
});
|
|
340
|
+
const instance = tree.root;
|
|
341
|
+
const viewButtonBottom = await makeValidateData(instance);
|
|
342
|
+
await act(async () => {
|
|
343
|
+
await viewButtonBottom.props.onRightClick();
|
|
344
|
+
});
|
|
345
|
+
expect(axios.post).toHaveBeenCalled();
|
|
346
|
+
expect(mockedNavigate).toHaveBeenCalledWith(Routes.AddCommonSelectSubUnit, {
|
|
347
|
+
...route.params,
|
|
348
|
+
});
|
|
349
|
+
});
|
|
202
350
|
});
|
|
@@ -2,6 +2,7 @@ import React, { useCallback, useContext, useEffect, useState } from 'react';
|
|
|
2
2
|
import { AppState, RefreshControl, View } from 'react-native';
|
|
3
3
|
import { useIsFocused } from '@react-navigation/native';
|
|
4
4
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
5
|
+
import { useNetInfo } from '@react-native-community/netinfo';
|
|
5
6
|
|
|
6
7
|
import styles from './styles';
|
|
7
8
|
import AddMenu from './AddMenu';
|
|
@@ -62,6 +63,7 @@ const UnitDetail = ({ route }) => {
|
|
|
62
63
|
automates: [],
|
|
63
64
|
});
|
|
64
65
|
const [isGGHomeConnected, setIsGGHomeConnected] = useState(false);
|
|
66
|
+
const [isNetworkConnected, setIsNetworkConnected] = useState(true);
|
|
65
67
|
const [station, setStation] = useState({});
|
|
66
68
|
const [indexStation, setIndexStation] = useState(0);
|
|
67
69
|
const [showAdd, setShowAdd, setHideAdd] = useBoolean();
|
|
@@ -73,6 +75,8 @@ const UnitDetail = ({ route }) => {
|
|
|
73
75
|
|
|
74
76
|
const { isOwner } = useIsOwnerOfUnit(unit.user_id);
|
|
75
77
|
|
|
78
|
+
const netInfo = useNetInfo();
|
|
79
|
+
|
|
76
80
|
const handleFullScreen = (data) => {
|
|
77
81
|
setIsFullScreen(!isFullScreen);
|
|
78
82
|
setDataFullScreen(data);
|
|
@@ -175,6 +179,10 @@ const UnitDetail = ({ route }) => {
|
|
|
175
179
|
};
|
|
176
180
|
}, [handleAppStateChange]);
|
|
177
181
|
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
setIsNetworkConnected(netInfo.isConnected);
|
|
184
|
+
}, [netInfo.isConnected]);
|
|
185
|
+
|
|
178
186
|
const handleGoogleHomeConnect = useCallback(
|
|
179
187
|
async (options) => {
|
|
180
188
|
let isConnected = await googleHomeConnect(options); // this may wrong if have multiple connection
|
|
@@ -281,6 +289,7 @@ const UnitDetail = ({ route }) => {
|
|
|
281
289
|
isOwner={isOwner}
|
|
282
290
|
favorites={favorites}
|
|
283
291
|
wrapItemStyle={styles.wrapItemStyle}
|
|
292
|
+
isNetworkConnected={isNetworkConnected}
|
|
284
293
|
isGGHomeConnected={isGGHomeConnected}
|
|
285
294
|
/>
|
|
286
295
|
);
|
|
@@ -307,6 +316,7 @@ const UnitDetail = ({ route }) => {
|
|
|
307
316
|
<ShortDetailSubUnit
|
|
308
317
|
unit={unit}
|
|
309
318
|
station={station}
|
|
319
|
+
isNetworkConnected={isNetworkConnected}
|
|
310
320
|
isGGHomeConnected={isGGHomeConnected}
|
|
311
321
|
/>
|
|
312
322
|
);
|
|
@@ -26,6 +26,16 @@ jest.mock('../../../iot/RemoteControl/GoogleHome', () => ({
|
|
|
26
26
|
|
|
27
27
|
jest.mock('axios');
|
|
28
28
|
|
|
29
|
+
jest.mock('@react-native-community/netinfo', () => {
|
|
30
|
+
return {
|
|
31
|
+
useNetInfo: () => {
|
|
32
|
+
return {
|
|
33
|
+
isConnected: true,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
|
|
29
39
|
const wrapComponent = (route, unitData, account) => (
|
|
30
40
|
<SCProvider initState={mockSCStore({})}>
|
|
31
41
|
<UnitDetail
|
|
@@ -62,6 +62,16 @@ jest.mock('home-assistant-js-websocket', () => {
|
|
|
62
62
|
};
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
+
jest.mock('@react-native-community/netinfo', () => {
|
|
66
|
+
return {
|
|
67
|
+
useNetInfo: () => {
|
|
68
|
+
return {
|
|
69
|
+
isConnected: true,
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
|
|
65
75
|
jest.mock('axios');
|
|
66
76
|
|
|
67
77
|
describe('Test UnitDetail', () => {
|
|
@@ -12,10 +12,15 @@ import { mockSCStore } from '../../../../context/mockStore';
|
|
|
12
12
|
|
|
13
13
|
jest.mock('axios');
|
|
14
14
|
|
|
15
|
+
const mockRenewItem = jest.fn();
|
|
15
16
|
const wrapComponent = (item, navigation) => (
|
|
16
17
|
<SCProvider initState={mockSCStore({})}>
|
|
17
|
-
<SharedUnit
|
|
18
|
-
|
|
18
|
+
<SharedUnit
|
|
19
|
+
item={item}
|
|
20
|
+
navigation={navigation}
|
|
21
|
+
renewItem={mockRenewItem}
|
|
22
|
+
index={0}
|
|
23
|
+
/>
|
|
19
24
|
</SCProvider>
|
|
20
25
|
);
|
|
21
26
|
|
|
@@ -55,6 +60,13 @@ describe('Test SharedUnit', () => {
|
|
|
55
60
|
|
|
56
61
|
test('test create SharedUnit unit is not pin, not star', async () => {
|
|
57
62
|
const navigation = useNavigation();
|
|
63
|
+
const response = {
|
|
64
|
+
status: 200,
|
|
65
|
+
success: true,
|
|
66
|
+
};
|
|
67
|
+
axios.post.mockImplementation(async () => {
|
|
68
|
+
return response;
|
|
69
|
+
});
|
|
58
70
|
|
|
59
71
|
await act(async () => {
|
|
60
72
|
tree = await create(wrapComponent(item, navigation));
|
|
@@ -100,6 +112,13 @@ describe('Test SharedUnit', () => {
|
|
|
100
112
|
const navigation = useNavigation();
|
|
101
113
|
item.is_pin = true;
|
|
102
114
|
item.is_star = true;
|
|
115
|
+
const response = {
|
|
116
|
+
status: 200,
|
|
117
|
+
success: true,
|
|
118
|
+
};
|
|
119
|
+
axios.post.mockImplementation(async () => {
|
|
120
|
+
return response;
|
|
121
|
+
});
|
|
103
122
|
|
|
104
123
|
await act(async () => {
|
|
105
124
|
tree = await create(wrapComponent(item, navigation));
|
|
@@ -43,10 +43,10 @@ describe('Test UnitSummary', () => {
|
|
|
43
43
|
Date.now = jest.fn(() => new Date('2021-01-24T12:00:00.000Z'));
|
|
44
44
|
route = {
|
|
45
45
|
params: {
|
|
46
|
-
|
|
46
|
+
unitData: {
|
|
47
47
|
id: 1,
|
|
48
48
|
},
|
|
49
|
-
|
|
49
|
+
summaryData: {
|
|
50
50
|
id: 1,
|
|
51
51
|
name: '',
|
|
52
52
|
screen: Routes.AirQuality,
|
|
@@ -155,7 +155,7 @@ describe('Test UnitSummary', () => {
|
|
|
155
155
|
|
|
156
156
|
list_value.forEach((value, index) => {
|
|
157
157
|
test(`create Unit Summarty Detail ${value}`, async () => {
|
|
158
|
-
route.params.
|
|
158
|
+
route.params.summaryData.screen = value;
|
|
159
159
|
await act(async () => {
|
|
160
160
|
tree = await create(wrapComponent(route));
|
|
161
161
|
});
|
|
@@ -113,10 +113,10 @@ describe('test RunningDevices', () => {
|
|
|
113
113
|
});
|
|
114
114
|
expect(mockedNavigate).toBeCalledWith('DeviceDetail', {
|
|
115
115
|
isGGHomeConnected: false,
|
|
116
|
-
|
|
116
|
+
sensorData: summaryDetail.devices[0],
|
|
117
117
|
station: 'station',
|
|
118
118
|
title: undefined,
|
|
119
|
-
|
|
119
|
+
unitData: unit,
|
|
120
120
|
});
|
|
121
121
|
});
|
|
122
122
|
});
|
|
@@ -23,15 +23,17 @@ import { timeDifference } from '../../utils/Converter/time';
|
|
|
23
23
|
|
|
24
24
|
const UnitSummary = memo(({ route }) => {
|
|
25
25
|
const t = useTranslations();
|
|
26
|
-
const {
|
|
26
|
+
const { unitData, unitId, summaryData, summaryId } = route.params;
|
|
27
27
|
const [summaryDetail, setSummaryDetail] = useState({});
|
|
28
|
+
const [unit, setUnit] = useState(unitData || { id: unitId });
|
|
29
|
+
const [summary, setSummary] = useState(summaryData || { id: summaryId });
|
|
28
30
|
|
|
29
31
|
const [loading, setLoading] = useState(false);
|
|
30
32
|
const [lastUpdated, setLastUpdated] = useState();
|
|
31
33
|
const navigation = useNavigation();
|
|
32
34
|
|
|
33
35
|
const getComponentAndGuide = useCallback(() => {
|
|
34
|
-
switch (summary
|
|
36
|
+
switch (summary?.screen) {
|
|
35
37
|
case Routes.AirQuality:
|
|
36
38
|
return {
|
|
37
39
|
guideName: Routes.AQIGuide,
|
|
@@ -66,12 +68,17 @@ const UnitSummary = memo(({ route }) => {
|
|
|
66
68
|
return {
|
|
67
69
|
componentName: ThreePhasePowerConsumption,
|
|
68
70
|
};
|
|
71
|
+
default:
|
|
72
|
+
return {
|
|
73
|
+
guideName: null,
|
|
74
|
+
componentName: null,
|
|
75
|
+
};
|
|
69
76
|
}
|
|
70
|
-
}, [summary
|
|
77
|
+
}, [summary?.screen]);
|
|
71
78
|
|
|
72
79
|
const fetchSummaryDetail = useCallback(async () => {
|
|
73
80
|
const { success, data } = await axiosGet(
|
|
74
|
-
API.UNIT.UNIT_SUMMARY_DETAIL(unit
|
|
81
|
+
API.UNIT.UNIT_SUMMARY_DETAIL(unit?.id, summary?.id),
|
|
75
82
|
{},
|
|
76
83
|
true
|
|
77
84
|
);
|
|
@@ -80,7 +87,41 @@ const UnitSummary = memo(({ route }) => {
|
|
|
80
87
|
setSummaryDetail(data.data);
|
|
81
88
|
setLastUpdated(data.data.last_updated);
|
|
82
89
|
}
|
|
83
|
-
}, [summary
|
|
90
|
+
}, [summary?.id, unit?.id]);
|
|
91
|
+
|
|
92
|
+
const fetchUnitDetail = useCallback(async () => {
|
|
93
|
+
const { success, data } = await axiosGet(
|
|
94
|
+
API.UNIT.UNIT_DETAIL(unit?.id),
|
|
95
|
+
{},
|
|
96
|
+
true
|
|
97
|
+
);
|
|
98
|
+
if (success) {
|
|
99
|
+
setUnit(data);
|
|
100
|
+
}
|
|
101
|
+
}, [unit.id]);
|
|
102
|
+
|
|
103
|
+
const fetchUnitSummary = useCallback(async () => {
|
|
104
|
+
const { success, data } = await axiosGet(
|
|
105
|
+
API.UNIT.UNIT_SUMMARY(unit?.id),
|
|
106
|
+
{}
|
|
107
|
+
);
|
|
108
|
+
if (success) {
|
|
109
|
+
const newData = data.filter((item) => item.id === summaryId);
|
|
110
|
+
setSummary(newData[0] || {});
|
|
111
|
+
}
|
|
112
|
+
}, [summaryId, unit?.id]);
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!unitData && unitId) {
|
|
116
|
+
fetchUnitDetail();
|
|
117
|
+
}
|
|
118
|
+
}, [fetchUnitDetail, unitData, unitId]);
|
|
119
|
+
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (!summaryData && summaryId) {
|
|
122
|
+
fetchUnitSummary();
|
|
123
|
+
}
|
|
124
|
+
}, [fetchUnitSummary, summaryData, summaryId]);
|
|
84
125
|
|
|
85
126
|
useEffect(() => {
|
|
86
127
|
setLoading(true);
|
|
@@ -99,8 +140,8 @@ const UnitSummary = memo(({ route }) => {
|
|
|
99
140
|
}, [fetchSummaryDetail]);
|
|
100
141
|
|
|
101
142
|
const UnitSummaryDetail = getComponentAndGuide();
|
|
102
|
-
const ComponentName = UnitSummaryDetail
|
|
103
|
-
const GuideName = UnitSummaryDetail
|
|
143
|
+
const ComponentName = UnitSummaryDetail?.componentName;
|
|
144
|
+
const GuideName = UnitSummaryDetail?.guideName;
|
|
104
145
|
|
|
105
146
|
const lastUpdatedStr = lastUpdated
|
|
106
147
|
? `${t('last_updated')} ${timeDifference(moment(), moment(lastUpdated))}`
|
|
@@ -109,7 +150,7 @@ const UnitSummary = memo(({ route }) => {
|
|
|
109
150
|
return (
|
|
110
151
|
<View style={[styles.container]}>
|
|
111
152
|
<WrapHeaderScrollable
|
|
112
|
-
title={summary
|
|
153
|
+
title={summary?.name}
|
|
113
154
|
subTitle={lastUpdatedStr}
|
|
114
155
|
rightComponent={
|
|
115
156
|
GuideName ? (
|
|
@@ -130,7 +171,9 @@ const UnitSummary = memo(({ route }) => {
|
|
|
130
171
|
loading={loading}
|
|
131
172
|
onRefresh={onRefresh}
|
|
132
173
|
>
|
|
133
|
-
|
|
174
|
+
{ComponentName && (
|
|
175
|
+
<ComponentName summaryDetail={summaryDetail} unit={unit} />
|
|
176
|
+
)}
|
|
134
177
|
</WrapHeaderScrollable>
|
|
135
178
|
</View>
|
|
136
179
|
);
|
|
@@ -668,6 +668,13 @@
|
|
|
668
668
|
"text_notification_content_pay_fine_successfully": "Your violation **%{booking}** has been paid successfully.",
|
|
669
669
|
"text_notification_content_pay_fine_and_extend_successfully": "Your violation **%{booking_id_old}** has been paid successfully. The new parking session **%{booking_id_new}** is now extended until **%{leave_time}**.",
|
|
670
670
|
"text_notification_content_stop_violation_free_parking_zone": "Free parking zone. You have an unpaid violation. Please complete your fine.",
|
|
671
|
+
"text_notification_content_air_quality_high": "The Air Quality index at **%{unit_name}** is **%{status}**. Wear a mask and reduce outdoor exercise.",
|
|
672
|
+
"text_notification_content_uv_index_high": "The UV index at **%{unit_name}** is **%{status}**. Reduce time in the sun and protect your skin, eye.",
|
|
673
|
+
"text_notification_content_pH_index_high": "The pH index at **%{unit_name}** is **%{status}**. Water is not safe to drink.",
|
|
674
|
+
"text_notification_content_clo_high": "The Chlorine residual at **%{unit_name}** is **%{status}**. Water is not safe to drink.",
|
|
675
|
+
"text_notification_content_turbility_high": "The Turbility at **%{unit_name}** is **%{status}**. Water is not safe to drink.",
|
|
676
|
+
"text_notification_content_smoke": "Smoke appears in **%{unit_name}**. Please check your home and call the rescue team if there is a fire.",
|
|
677
|
+
"text_notification_content_fire": "There is a fire at **%{unit_name}**, Please move out of the house immediately and call the rescue team.",
|
|
671
678
|
"text_notification_content_remove_unit_to_owner": "Unit **%{unit_name}** has been removed successfully.",
|
|
672
679
|
"text_notification_content_remove_unit_to_member": "Unit **%{unit_name}** has been removed by **%{unit_owner_name}**. You cannot access to this unit anymore.",
|
|
673
680
|
"text_notification_content_remove_member": "You were remove from **%{unit_name}** by **%{unit_owner_name}**. You cannot access to this unit anymore.",
|
|
@@ -676,6 +676,13 @@
|
|
|
676
676
|
"text_notification_content_pay_fine_successfully": "Đỗ xe vi phạm **%{booking}** của bạn đã được thanh toán.",
|
|
677
677
|
"text_notification_content_pay_fine_and_extend_successfully": "Đỗ xe vi phạm **%{oldbooking}** của bạn đã được thanh toán thành công. Phiên đỗ xe mới **%{newbooking}** sẽ kết thúc vào lúc **%{time}**.",
|
|
678
678
|
"text_notification_content_stop_violation_free_parking_zone": "Thời gian đậu xe miễn phí. Bạn có một vi phạm chưa được thanh toán. Vui lòng hoàn thành tiền phạt của bạn.",
|
|
679
|
+
"text_notification_content_air_quality_high": "Chỉ số AQI tại **%{unit_name}** đang ở mức **%{status}**. Đeo khẩu trang và giảm các hoạt động ngoài trời để bảo vệ sức khoẻ của bạn.",
|
|
680
|
+
"text_notification_content_uv_index_high": "Chỉ số UV tại **%{unit_name}** đang ở mức **%{status}**. Giảm thời gian ngoài trời và bảo vệ da, mắt khỏi ánh nắng mặt trời.",
|
|
681
|
+
"text_notification_content_pH_index_high": "Độ pH của nước tại **%{unit_name}** đang ở mức **%{status}**. Nước không đảm bảo an toàn để uống.",
|
|
682
|
+
"text_notification_content_clo_high": "Độ clo của nước tại **%{unit_name}** đang ở mức **%{status}**. Nước không đảm bảo an toàn để uống.",
|
|
683
|
+
"text_notification_content_turbility_high": "Độ đục của nước tại **%{unit_name}** đang ở mức **%{status}**. Nước không đảm bảo an toàn để uống.",
|
|
684
|
+
"text_notification_content_smoke": "Xuất hiện khói tại **%{unit_name}**. Vui lòng kiểm tra nhà của bạn và gọi cho đội cứu hộ nếu xảy ra hỏa hoạn.",
|
|
685
|
+
"text_notification_content_fire": "Có đám cháy tại **%{unit_name}**, Vui lòng di chuyển ra khỏi nhà ngay lập tức và gọi cho đội cứu hộ.",
|
|
679
686
|
"text_notification_content_remove_unit_to_owner": "Địa điểm **%{unit_name}** vừa được xoá thành công.",
|
|
680
687
|
"text_notification_content_remove_unit_to_member": "Địa điểm **%{unit_name}** vừa được xoá bởi **%{unit_owner_name}**. Bạn không thể truy cập vào địa điểm này được nữa.",
|
|
681
688
|
"text_notification_content_remove_member": "Bạn vừa được xoá khỏi **%{unit_name}** bởi **%{unit_owner_name}**. Bạn không thể truy cập vào địa điểm này được nữa.",
|