@eohjsc/react-native-smart-city 0.2.79 → 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 +4 -4
- 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 +8 -2
- package/src/commons/Device/ItemDevice.js +8 -5
- package/src/commons/Device/LinearChart.js +1 -1
- package/src/commons/EmergencyButton/AlertSendConfirm.js +2 -2
- package/src/commons/EmergencyButton/AlertSent.js +2 -2
- package/src/commons/ImagePicker/__test__/ImagePicker.test.js +24 -3
- package/src/commons/MediaPlayerDetail/index.js +1 -0
- 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 +5 -0
- package/src/iot/RemoteControl/Internet.js +8 -1
- package/src/iot/RemoteControl/index.js +4 -2
- package/src/screens/AddCommon/SelectSubUnit.js +6 -0
- package/src/screens/AddNewGateway/__test__/SetupGateway.test.js +52 -0
- package/src/screens/AllCamera/index.js +76 -44
- 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 +147 -3
- package/src/screens/Notification/index.js +4 -1
- package/src/screens/Notification/styles/NotificationItemStyles.js +3 -3
- package/src/screens/Sharing/SelectUser.js +17 -10
- package/src/screens/Sharing/__test__/SelectUser.test.js +73 -0
- package/src/screens/SubUnit/AddSubUnit.js +4 -1
- package/src/screens/SubUnit/__test__/AddSubUnit.test.js +148 -0
- package/src/screens/Unit/Detail.js +28 -4
- package/src/screens/Unit/ManageUnit.js +88 -32
- package/src/screens/Unit/ManageUnitStyles.js +20 -0
- package/src/screens/Unit/SmartAccount.js +6 -2
- 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 +53 -0
- package/src/screens/Unit/__test__/ManageUnit.test.js +69 -0
- package/src/screens/Unit/__test__/SmartAccount.test.js +37 -8
- package/src/screens/Unit/components/__test__/SharedUnit.test.js +21 -2
- package/src/screens/Unit/hook/useStateAlertRemove.js +1 -1
- 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/Apis/axios.js +7 -2
- package/src/utils/I18n/translations/en.json +10 -1
- package/src/utils/I18n/translations/vi.json +10 -1
- package/src/{screens/Notification → utils}/Monitor.js +1 -1
|
@@ -11,6 +11,17 @@ import { mockSCStore } from '../../../context/mockStore';
|
|
|
11
11
|
|
|
12
12
|
jest.mock('axios');
|
|
13
13
|
|
|
14
|
+
const mockNavigate = jest.fn();
|
|
15
|
+
jest.mock('@react-navigation/native', () => {
|
|
16
|
+
return {
|
|
17
|
+
...jest.requireActual('@react-navigation/native'),
|
|
18
|
+
useRoute: jest.fn(),
|
|
19
|
+
useNavigation: () => ({
|
|
20
|
+
navigate: mockNavigate,
|
|
21
|
+
}),
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
14
25
|
const mockedDispatch = jest.fn();
|
|
15
26
|
jest.mock('react-redux', () => ({
|
|
16
27
|
useSelector: jest.fn(),
|
|
@@ -42,6 +53,11 @@ describe('Test Manage Unit', () => {
|
|
|
42
53
|
},
|
|
43
54
|
};
|
|
44
55
|
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
mockNavigate.mockClear();
|
|
58
|
+
axios.get.mockClear();
|
|
59
|
+
});
|
|
60
|
+
|
|
45
61
|
const getElement = (instance) => {
|
|
46
62
|
const changeName = instance.findAll(
|
|
47
63
|
(item) => item.props.testID === TESTID.MANAGE_UNIT_CHANGE_NAME
|
|
@@ -207,4 +223,57 @@ describe('Test Manage Unit', () => {
|
|
|
207
223
|
spyToast.mockReset();
|
|
208
224
|
spyToast.mockRestore();
|
|
209
225
|
});
|
|
226
|
+
test('test onPress onUpdateImage', async () => {
|
|
227
|
+
await act(async () => {
|
|
228
|
+
tree = create(wrapComponent(route));
|
|
229
|
+
});
|
|
230
|
+
const instance = tree.root;
|
|
231
|
+
|
|
232
|
+
const imagePicker = instance.find(
|
|
233
|
+
(item) => item.props.testID === TESTID.MANAGE_UNIT_IMAGE_PICKER
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
const handleChoosePhoto = instance.findAll(
|
|
237
|
+
(item) => item.props.testID === TESTID.MANAGE_UNIT_CHANGE_PHOTO
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
act(() => {
|
|
241
|
+
handleChoosePhoto[0].props.onPress();
|
|
242
|
+
handleChoosePhoto[1].props.onPress();
|
|
243
|
+
imagePicker.props.setImageUrl('abc');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
expect(axios.patch).toBeCalled();
|
|
247
|
+
expect(imagePicker.props.showImagePicker).toBeTruthy();
|
|
248
|
+
});
|
|
249
|
+
test('test onPress goSelectLocation', async () => {
|
|
250
|
+
await act(async () => {
|
|
251
|
+
tree = create(wrapComponent(route));
|
|
252
|
+
});
|
|
253
|
+
const instance = tree.root;
|
|
254
|
+
|
|
255
|
+
const buttonWrapper = instance.find(
|
|
256
|
+
(item) => item.props.testID === TESTID.MANAGE_UNIT_CHANGE_LOCATION
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
act(() => {
|
|
260
|
+
buttonWrapper.props.onPress();
|
|
261
|
+
});
|
|
262
|
+
expect(mockNavigate).toBeCalled();
|
|
263
|
+
});
|
|
264
|
+
test('test onPress goToManageSubUnit', async () => {
|
|
265
|
+
await act(async () => {
|
|
266
|
+
tree = create(wrapComponent(route));
|
|
267
|
+
});
|
|
268
|
+
const instance = tree.root;
|
|
269
|
+
|
|
270
|
+
const buttonWrapper = instance.find(
|
|
271
|
+
(item) => item.props.testID === TESTID.MANAGE_UNIT_GO_TO_SUBUNIT
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
act(() => {
|
|
275
|
+
buttonWrapper.props.onPress();
|
|
276
|
+
});
|
|
277
|
+
expect(mockNavigate).toBeCalled();
|
|
278
|
+
});
|
|
210
279
|
});
|
|
@@ -6,21 +6,22 @@ import { SCProvider } from '../../../context';
|
|
|
6
6
|
import { mockSCStore } from '../../../context/mockStore';
|
|
7
7
|
import ListSmartAccount from '../SmartAccount';
|
|
8
8
|
import { SmartAccountItem } from '../SmartAccountItem';
|
|
9
|
-
import { MenuActionMore } from '../../../commons';
|
|
9
|
+
import { AlertAction, MenuActionMore } from '../../../commons';
|
|
10
|
+
import Routes from '../../../utils/Route';
|
|
10
11
|
|
|
11
12
|
const wrapComponent = (route, navigation) => (
|
|
12
13
|
<SCProvider initState={mockSCStore({})}>
|
|
13
|
-
<ListSmartAccount />
|
|
14
|
+
<ListSmartAccount route={route} />
|
|
14
15
|
</SCProvider>
|
|
15
16
|
);
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
-
|
|
18
|
+
const mockNavigate = jest.fn();
|
|
19
19
|
jest.mock('@react-navigation/native', () => {
|
|
20
20
|
return {
|
|
21
21
|
...jest.requireActual('@react-navigation/native'),
|
|
22
|
+
useRoute: jest.fn(),
|
|
22
23
|
useNavigation: () => ({
|
|
23
|
-
|
|
24
|
+
navigate: mockNavigate,
|
|
24
25
|
}),
|
|
25
26
|
};
|
|
26
27
|
});
|
|
@@ -35,8 +36,13 @@ jest.mock('react', () => {
|
|
|
35
36
|
jest.mock('axios');
|
|
36
37
|
describe('Test SmartAccount', () => {
|
|
37
38
|
let tree;
|
|
39
|
+
let route = {
|
|
40
|
+
params: {
|
|
41
|
+
unitId: 1,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
38
44
|
test('test render SmartAccount', async () => {
|
|
39
|
-
|
|
45
|
+
const reponse = {
|
|
40
46
|
status: 200,
|
|
41
47
|
data: [
|
|
42
48
|
{
|
|
@@ -58,14 +64,25 @@ describe('Test SmartAccount', () => {
|
|
|
58
64
|
username: 'loitest3',
|
|
59
65
|
},
|
|
60
66
|
],
|
|
61
|
-
}
|
|
67
|
+
};
|
|
68
|
+
axios.get.mockImplementationOnce(() => reponse);
|
|
62
69
|
await act(async () => {
|
|
63
|
-
tree = await renderer.create(wrapComponent());
|
|
70
|
+
tree = await renderer.create(wrapComponent(route));
|
|
64
71
|
});
|
|
65
72
|
const instance = tree.root;
|
|
66
73
|
|
|
67
74
|
const smartAccountItem = instance.findAllByType(SmartAccountItem);
|
|
68
75
|
expect(smartAccountItem.length).toEqual(3);
|
|
76
|
+
act(() => {
|
|
77
|
+
smartAccountItem[0].props.gotoSmartAccountDetail(reponse.data[0]);
|
|
78
|
+
smartAccountItem[0].props.onShowMenuMore(reponse.data[0]);
|
|
79
|
+
});
|
|
80
|
+
expect(mockNavigate).toBeCalledWith(Routes.ListDeviceSmartAccount, {
|
|
81
|
+
username: reponse.data[0].username,
|
|
82
|
+
brand: reponse.data[0].brand,
|
|
83
|
+
smart_account_id: reponse.data[0].id,
|
|
84
|
+
unit_id: route.params.unitId,
|
|
85
|
+
});
|
|
69
86
|
});
|
|
70
87
|
|
|
71
88
|
test('test render SmartAccountItem', async () => {
|
|
@@ -79,4 +96,16 @@ describe('Test SmartAccount', () => {
|
|
|
79
96
|
});
|
|
80
97
|
expect(menuActionMore.props.isVisible).toEqual(false);
|
|
81
98
|
});
|
|
99
|
+
|
|
100
|
+
test('test render AlertAction', async () => {
|
|
101
|
+
await act(async () => {
|
|
102
|
+
tree = await renderer.create(wrapComponent());
|
|
103
|
+
});
|
|
104
|
+
const instance = tree.root;
|
|
105
|
+
const alertAction = instance.findByType(AlertAction);
|
|
106
|
+
await act(async () => {
|
|
107
|
+
alertAction.props.leftButtonClick();
|
|
108
|
+
});
|
|
109
|
+
expect(alertAction).toBeDefined();
|
|
110
|
+
});
|
|
82
111
|
});
|
|
@@ -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
|
);
|
package/src/utils/Apis/axios.js
CHANGED
|
@@ -151,7 +151,10 @@ export async function axiosPatch(...options) {
|
|
|
151
151
|
export async function axiosDelete(...options) {
|
|
152
152
|
return await axiosCall('delete', ...options);
|
|
153
153
|
}
|
|
154
|
-
|
|
154
|
+
const convertFilenameImage = (filename) => {
|
|
155
|
+
const filenameConverted = filename?.replace(/HEIC/g, 'jpg');
|
|
156
|
+
return filename || filenameConverted;
|
|
157
|
+
};
|
|
155
158
|
export function createFormData(data, list_file_field) {
|
|
156
159
|
const formData = new FormData();
|
|
157
160
|
|
|
@@ -165,7 +168,9 @@ export function createFormData(data, list_file_field) {
|
|
|
165
168
|
}
|
|
166
169
|
|
|
167
170
|
formData.append(key, {
|
|
168
|
-
name:
|
|
171
|
+
name: convertFilenameImage(
|
|
172
|
+
item.filename || item.path?.split('/').pop()
|
|
173
|
+
),
|
|
169
174
|
type: item.mime,
|
|
170
175
|
uri: item.path,
|
|
171
176
|
});
|
|
@@ -668,10 +668,18 @@
|
|
|
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.",
|
|
674
681
|
"text_notification_content_member_leave_unit": "**%{member_name}** has left **%{unit_name}**.",
|
|
682
|
+
"text_notification_content_rename_unit": "Unit **%{old_unit_name}** has been renamed to **%{new_unit_name}** by **%{owner_name}**.",
|
|
675
683
|
"this_spot_does_not_exsit": "This spot does not exist",
|
|
676
684
|
"please_scan_again_or_contact_the_parking_manager": "Please scan again or contact the parking manager",
|
|
677
685
|
"this_spot_does_not_support_to_scan": "This spot does not support to scan",
|
|
@@ -894,8 +902,9 @@
|
|
|
894
902
|
"location_permission_required_wifi_message": "This app needs location permission as this is required to scan for wifi networks.",
|
|
895
903
|
"deny": "DENY",
|
|
896
904
|
"allow": "ALLOW",
|
|
905
|
+
"avatar" : "Avatar",
|
|
897
906
|
"error_share_permission": "{text} {data} does not exist!",
|
|
898
907
|
"text_phone_share_permission": "The phone",
|
|
899
908
|
"text_email_share_permission": "Email",
|
|
900
|
-
"
|
|
909
|
+
"icon_unit": "Icon unit"
|
|
901
910
|
}
|
|
@@ -676,10 +676,18 @@
|
|
|
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.",
|
|
682
689
|
"text_notification_content_member_leave_unit": "**%{member_name}** vừa rời khỏi **%{unit_name}**.",
|
|
690
|
+
"text_notification_content_rename_unit": "Địa điểm **%{old_unit_name}** vừa được đổi tên thành **%{new_unit_name}** bởi **%{owner_name}**.",
|
|
683
691
|
"this_spot_does_not_exsit": "Vị trí đỗ này không tồn tại",
|
|
684
692
|
"please_scan_again_or_contact_the_parking_manager": "Vui lòng quét lại hoặc liên hệ với người quản lý bãi đậu xe",
|
|
685
693
|
"this_spot_does_not_support_to_scan": "Vị trí đỗ này không hỗ trợ quét",
|
|
@@ -897,8 +905,9 @@
|
|
|
897
905
|
"location_permission_required_wifi_message": "Ứng dụng này cần có quyền định vị vì ứng dụng này được yêu cầu để quét các mạng Wi-Fi.",
|
|
898
906
|
"deny": "Từ chối",
|
|
899
907
|
"allow": "Cho phép",
|
|
908
|
+
"avatar" : "Ảnh đại diện",
|
|
900
909
|
"error_share_permission": "{text} {data} không tồn tại!",
|
|
901
910
|
"text_phone_share_permission": "Số điện thoại",
|
|
902
911
|
"text_email_share_permission": "Email",
|
|
903
|
-
"
|
|
912
|
+
"icon_unit": "Ảnh đại diện địa điểm"
|
|
904
913
|
}
|