@eohjsc/react-native-smart-city 0.7.41 → 0.7.43

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 (35) hide show
  1. package/package.json +1 -1
  2. package/src/commons/Dashboard/MyDashboardDevice/index.js +14 -12
  3. package/src/commons/Dashboard/MyUnit/index.js +4 -3
  4. package/src/commons/DateTimeRangeChange/index.js +5 -5
  5. package/src/commons/Device/HorizontalBarChart.js +11 -24
  6. package/src/commons/Device/PowerConsumptionChart.js +224 -100
  7. package/src/commons/Form/CurrencyInput.js +9 -18
  8. package/src/commons/SubUnit/Favorites/index.js +35 -10
  9. package/src/commons/SubUnit/ShortDetail.js +17 -4
  10. package/src/commons/SubUnit/__test__/Favorites.test.js +90 -0
  11. package/src/commons/SubUnit/__test__/ShortDetail.test.js +45 -2
  12. package/src/configs/AccessibilityLabel.js +1 -0
  13. package/src/hooks/IoT/__test__/useValueEvaluation.test.js +12 -12
  14. package/src/iot/UpdateStates.js +37 -16
  15. package/src/iot/mqtt.js +26 -1
  16. package/src/navigations/UnitStack.js +2 -2
  17. package/src/screens/ActivityLog/ItemLog.js +69 -16
  18. package/src/screens/ActivityLog/__test__/ItemLog.test.js +169 -2
  19. package/src/screens/ActivityLog/index.js +2 -2
  20. package/src/screens/ActivityLog/styles/itemLogStyles.js +35 -0
  21. package/src/screens/Device/__test__/DeviceDetail-3rdparty.test.js +16 -5
  22. package/src/screens/Device/__test__/DeviceDetail-arduino.test.js +10 -0
  23. package/src/screens/Device/__test__/DeviceDetail-modbus.test.js +37 -8
  24. package/src/screens/Device/__test__/DeviceDetail-zigbee.test.js +16 -5
  25. package/src/screens/Device/__test__/DeviceDetail.test.js +17 -6
  26. package/src/screens/Device/__test__/sensorDisplayItem.test.js +47 -11
  27. package/src/screens/Device/components/SensorDisplayItem.js +24 -5
  28. package/src/screens/Device/styles.js +4 -0
  29. package/src/screens/UnitSummary/components/3PPowerConsumption/index.js +8 -49
  30. package/src/screens/UnitSummary/components/PowerConsumption/__test__/PowerConsumption.test.js +153 -6
  31. package/src/screens/UnitSummary/components/PowerConsumption/index.js +6 -47
  32. package/src/utils/I18n/translations/en.js +2 -0
  33. package/src/utils/I18n/translations/vi.js +2 -0
  34. package/src/utils/chartHelper/index.js +53 -0
  35. package/src/utils/chartHelper/getMaxValueIndex.js +0 -11
@@ -4,6 +4,10 @@ import { useNavigation } from '@react-navigation/native';
4
4
  import { useTranslations } from '../../../hooks/Common/useTranslations';
5
5
  import { useDevicesStatus } from '../../../hooks/Common';
6
6
  import { useWatchConfigs } from '../../../hooks/IoT';
7
+ import useChipJsonConfiguration, {
8
+ useConnectChipMqtt,
9
+ } from '../../../hooks/useMqtt';
10
+ import useWatchSharedChips from '../../../hooks/IoT/useWatchSharedChips';
7
11
 
8
12
  import { Section } from '../../Section';
9
13
  import ItemDevice from '../../Device/ItemDevice';
@@ -11,7 +15,6 @@ import ItemOneTap from '../OneTap/ItemOneTap';
11
15
  import ItemAddNew from '../../Device/ItemAddNew';
12
16
  import styles from './styles';
13
17
  import Routes from '../../../utils/Route';
14
- import { DEVICE_TYPE } from '../../../configs/Constants';
15
18
 
16
19
  const SubUnitFavorites = ({
17
20
  isOwner,
@@ -25,22 +28,44 @@ const SubUnitFavorites = ({
25
28
 
26
29
  useDevicesStatus(unit, favoriteDevices);
27
30
 
31
+ const favoriteChipIdsSet = useMemo(() => {
32
+ return new Set(favoriteDevices?.map((device) => device.chip_id));
33
+ }, [favoriteDevices]);
34
+
35
+ const { chips, isFetching } = useChipJsonConfiguration({
36
+ dashboardId: unit?.id,
37
+ });
38
+
39
+ const listChipsMqtt = useMemo(() => {
40
+ return chips.filter((chip) => favoriteChipIdsSet.has(chip.id));
41
+ }, [chips, favoriteChipIdsSet]);
42
+
43
+ const { mqttConfigs } = useConnectChipMqtt(listChipsMqtt);
44
+
28
45
  const configsNeedWatching = useMemo(() => {
29
46
  const configIds = [];
47
+
30
48
  favoriteDevices.forEach((device) => {
31
- if (
32
- device?.quick_action?.config_id &&
33
- ![DEVICE_TYPE.GOOGLE_HOME, DEVICE_TYPE.LG_THINQ].includes(
34
- device?.device_type
35
- )
36
- ) {
49
+ if (device?.quick_action?.config_id && device?.is_managed_by_backend) {
37
50
  configIds.push(device.quick_action.config_id);
38
51
  }
39
52
  });
40
- return configIds;
41
- }, [favoriteDevices]);
42
53
 
43
- useWatchConfigs(configsNeedWatching);
54
+ return configIds.filter((id) => !mqttConfigs[id]);
55
+ }, [favoriteDevices, mqttConfigs]);
56
+
57
+ useWatchConfigs(isFetching ? [] : configsNeedWatching);
58
+
59
+ const listSharedChipIds = useMemo(() => {
60
+ const chipMqttIdsSet = new Set(listChipsMqtt.map((chip) => chip.id));
61
+ return [...favoriteChipIdsSet].filter((id) => !chipMqttIdsSet.has(id));
62
+ }, [listChipsMqtt, favoriteChipIdsSet]);
63
+
64
+ useWatchSharedChips({
65
+ dashboardId: unit?.id,
66
+ filterChipIds: listSharedChipIds,
67
+ ready: !!listSharedChipIds.length,
68
+ });
44
69
 
45
70
  const handleOnAddNew = () => {
46
71
  navigate(Routes.SelectAddToFavorites, {
@@ -32,14 +32,17 @@ const ShortDetailSubUnit = ({ unit, station, isOwner }) => {
32
32
 
33
33
  useDevicesStatus(unit, station?.devices);
34
34
 
35
+ const stationChipIdsSet = useMemo(() => {
36
+ return new Set(station?.devices?.map((device) => device.chip_id));
37
+ }, [station?.devices]);
38
+
35
39
  const { chips, isFetching } = useChipJsonConfiguration({
36
40
  dashboardId: unit?.id,
37
41
  });
38
42
 
39
43
  const listChipsMqtt = useMemo(() => {
40
- const stationChipIds = station?.devices?.map((device) => device.chip_id);
41
- return chips?.filter((chip) => stationChipIds?.includes(chip.id));
42
- }, [chips, station?.devices]);
44
+ return chips.filter((chip) => stationChipIdsSet.has(chip.id));
45
+ }, [chips, stationChipIdsSet]);
43
46
 
44
47
  const { mqttConfigs } = useConnectChipMqtt(listChipsMqtt);
45
48
 
@@ -64,7 +67,17 @@ const ShortDetailSubUnit = ({ unit, station, isOwner }) => {
64
67
  }, [station, mqttConfigs]);
65
68
 
66
69
  useWatchConfigs(isFetching ? [] : configsNeedWatching);
67
- useWatchSharedChips({ dashboardId: unit?.id });
70
+
71
+ const listSharedChipIds = useMemo(() => {
72
+ const chipMqttIdsSet = new Set(listChipsMqtt.map((chip) => chip.id));
73
+ return [...stationChipIdsSet].filter((id) => !chipMqttIdsSet.has(id));
74
+ }, [listChipsMqtt, stationChipIdsSet]);
75
+
76
+ useWatchSharedChips({
77
+ dashboardId: unit?.id,
78
+ filterChipIds: listSharedChipIds,
79
+ ready: !!listSharedChipIds.length,
80
+ });
68
81
 
69
82
  const goToPlayBack = useCallback(() => {
70
83
  navigate(Routes.PlaybackCamera, {
@@ -1,12 +1,21 @@
1
1
  import React from 'react';
2
+ import MockAdapter from 'axios-mock-adapter';
2
3
  import { act, create } from 'react-test-renderer';
4
+ import { API } from '../../../configs';
3
5
  import { SCProvider } from '../../../context';
4
6
  import { mockSCStore } from '../../../context/mockStore';
7
+ import { watchMultiDataChips, watchMultiConfigs } from '../../../iot/Monitor';
8
+ import api from '../../../utils/Apis/axios';
9
+ import { gatewayDataFactory } from '../../../utils/FactoryGateway';
5
10
  import ItemAddNew from '../../Device/ItemAddNew';
6
11
  import ItemDevice from '../../Device/ItemDevice';
7
12
  import SubUnitFavorites from '../Favorites';
8
13
  import ItemOneTap from '../OneTap/ItemOneTap';
9
14
 
15
+ const mockAxios = new MockAdapter(api.axiosInstance);
16
+
17
+ jest.mock('../../../iot/Monitor');
18
+
10
19
  const wrapComponent = (props) => (
11
20
  <SCProvider initState={mockSCStore({})}>
12
21
  <SubUnitFavorites {...props} />
@@ -18,6 +27,7 @@ describe('test ShortDetail Subunit', () => {
18
27
  let props;
19
28
 
20
29
  beforeEach(() => {
30
+ jest.useFakeTimers();
21
31
  props = {
22
32
  unit: {
23
33
  address: null,
@@ -78,6 +88,8 @@ describe('test ShortDetail Subunit', () => {
78
88
  ],
79
89
  isGGHomeConnected: true,
80
90
  };
91
+ watchMultiConfigs.mockClear();
92
+ watchMultiDataChips.mockClear();
81
93
  });
82
94
 
83
95
  it('render SubUnitFavorites', async () => {
@@ -96,4 +108,82 @@ describe('test ShortDetail Subunit', () => {
96
108
  await itemAddNew[0].props.onAddNew();
97
109
  });
98
110
  });
111
+
112
+ it('render using mqtt call watch config and data chips', async () => {
113
+ mockAxios
114
+ .onGet(API.CHIP.JSON_CONFIGURATION)
115
+ .reply(200, [gatewayDataFactory]);
116
+ mockAxios
117
+ .onGet(API.CHIP.SHARED_CONFIGURATION)
118
+ .reply(200, [gatewayDataFactory, { ...gatewayDataFactory, id: 7690 }]);
119
+ props.favoriteDevices = [
120
+ {
121
+ action: {
122
+ color: '#00979D',
123
+ icon: 'caretup',
124
+ id: 1,
125
+ key: '',
126
+ },
127
+ action2: null,
128
+ chip_id: 7690,
129
+ description: null,
130
+ icon: '',
131
+ id: 1,
132
+ name: 'People Counting',
133
+ quick_action: { config_id: 128283 },
134
+ remote_control_options: {},
135
+ station: {},
136
+ status: null,
137
+ status2: null,
138
+ is_managed_by_backend: true,
139
+ },
140
+ ];
141
+
142
+ await act(async () => {
143
+ tree = await create(wrapComponent(props));
144
+ });
145
+ await act(async () => {
146
+ jest.runOnlyPendingTimers();
147
+ });
148
+ expect(watchMultiConfigs).toHaveBeenCalledWith([128283]);
149
+ expect(watchMultiDataChips).toHaveBeenCalledWith([7690]);
150
+ });
151
+
152
+ it('render using mqtt not call watch config and data chips', async () => {
153
+ mockAxios
154
+ .onGet(API.CHIP.JSON_CONFIGURATION)
155
+ .reply(200, [gatewayDataFactory]);
156
+ mockAxios.onGet(API.CHIP.SHARED_CONFIGURATION).reply(200, []);
157
+ props.favoriteDevices = [
158
+ {
159
+ action: {
160
+ color: '#00979D',
161
+ icon: 'caretup',
162
+ id: 1,
163
+ key: '',
164
+ },
165
+ action2: null,
166
+ chip_id: 7689,
167
+ description: null,
168
+ icon: '',
169
+ id: 1,
170
+ name: 'People Counting',
171
+ quick_action: { config_id: 128282 },
172
+ remote_control_options: {},
173
+ station: {},
174
+ status: null,
175
+ status2: null,
176
+ is_managed_by_backend: true,
177
+ },
178
+ ];
179
+
180
+ await act(async () => {
181
+ tree = await create(wrapComponent(props));
182
+ });
183
+ await act(async () => {
184
+ jest.runOnlyPendingTimers();
185
+ });
186
+ expect(watchMultiConfigs).not.toHaveBeenCalled();
187
+ expect(watchMultiDataChips).not.toHaveBeenCalled();
188
+ });
99
189
  });
@@ -7,7 +7,7 @@ import { API } from '../../../configs';
7
7
  import { AccessibilityLabel } from '../../../configs/Constants';
8
8
  import { SCProvider } from '../../../context';
9
9
  import { mockSCStore } from '../../../context/mockStore';
10
- import { watchMultiConfigs } from '../../../iot/Monitor';
10
+ import { watchMultiDataChips, watchMultiConfigs } from '../../../iot/Monitor';
11
11
  import { keyPermission } from '../../../utils/Permission/common';
12
12
  import api from '../../../utils/Apis/axios';
13
13
  import Routes from '../../../utils/Route';
@@ -78,6 +78,7 @@ describe('test ShortDetail Subunit', () => {
78
78
  isGGHomeConnected: true,
79
79
  };
80
80
  watchMultiConfigs.mockClear();
81
+ watchMultiDataChips.mockClear();
81
82
  });
82
83
 
83
84
  it('render ShortDetail', async () => {
@@ -191,10 +192,51 @@ describe('test ShortDetail Subunit', () => {
191
192
  expect(watchMultiConfigs).toHaveBeenCalledWith([1]);
192
193
  });
193
194
 
194
- it('render using mqtt not call watch config', async () => {
195
+ it('render using mqtt call watch config and data chips', async () => {
195
196
  mockAxios
196
197
  .onGet(API.CHIP.JSON_CONFIGURATION)
197
198
  .reply(200, [gatewayDataFactory]);
199
+ mockAxios
200
+ .onGet(API.CHIP.SHARED_CONFIGURATION)
201
+ .reply(200, [gatewayDataFactory, { ...gatewayDataFactory, id: 7690 }]);
202
+ props.station.devices = [
203
+ {
204
+ action: {
205
+ color: '#00979D',
206
+ icon: 'caretup',
207
+ id: 1,
208
+ key: '',
209
+ },
210
+ action2: null,
211
+ chip_id: 7690,
212
+ description: null,
213
+ icon: '',
214
+ id: 1,
215
+ name: 'People Counting',
216
+ quick_action: { config_id: 128283 },
217
+ remote_control_options: {},
218
+ station: {},
219
+ status: null,
220
+ status2: null,
221
+ is_managed_by_backend: true,
222
+ },
223
+ ];
224
+
225
+ await act(async () => {
226
+ tree = await create(wrapComponent(props));
227
+ });
228
+ await act(async () => {
229
+ jest.runOnlyPendingTimers();
230
+ });
231
+ expect(watchMultiConfigs).toHaveBeenCalledWith([128283]);
232
+ expect(watchMultiDataChips).toHaveBeenCalledWith([7690]);
233
+ });
234
+
235
+ it('render using mqtt not call watch config and data chips', async () => {
236
+ mockAxios
237
+ .onGet(API.CHIP.JSON_CONFIGURATION)
238
+ .reply(200, [gatewayDataFactory]);
239
+ mockAxios.onGet(API.CHIP.SHARED_CONFIGURATION).reply(200, []);
198
240
  props.station.devices = [
199
241
  {
200
242
  action: {
@@ -225,6 +267,7 @@ describe('test ShortDetail Subunit', () => {
225
267
  jest.runOnlyPendingTimers();
226
268
  });
227
269
  expect(watchMultiConfigs).not.toHaveBeenCalled();
270
+ expect(watchMultiDataChips).not.toHaveBeenCalled();
228
271
  });
229
272
 
230
273
  ['ConfigAndEvaluation', 'ConfigValue', 'EvaluationOverConfig'].forEach(
@@ -138,6 +138,7 @@ export default {
138
138
  BOTTOM_BUTTON_RIGHT: 'BOTTOM_BUTTON_RIGHT',
139
139
  VIEW_BUTTON_BOTTOM_RIGHT_BUTTON: 'VIEW_BUTTON_BOTTOM_RIGHT_BUTTON',
140
140
  VIEW_BUTTON_BOTTOM_LEFT_BUTTON: 'VIEW_BUTTON_BOTTOM_LEFT_BUTTON',
141
+ TOUCHABLE_OPACITY: 'TOUCHABLE_OPACITY',
141
142
 
142
143
  // Smart parking drawer
143
144
  ROW_EXIT_SMARTPARKING_DRAWER: 'ROW_EXIT_SMARTPARKING_DRAWER',
@@ -1,11 +1,11 @@
1
- import React from 'react';
2
- import { renderHook } from '@testing-library/react-hooks';
1
+ import { act, renderHook } from '@testing-library/react-hooks';
3
2
  import MockAdapter from 'axios-mock-adapter';
3
+ import React from 'react';
4
+ import { API } from '../../../configs';
4
5
  import { SCProvider } from '../../../context';
5
6
  import { mockSCStore } from '../../../context/mockStore';
6
- import { useValueEvaluations } from '../index';
7
7
  import api from '../../../utils/Apis/axios';
8
- import { API } from '../../../configs';
8
+ import { useValueEvaluations } from '../index';
9
9
 
10
10
  const mock = new MockAdapter(api.axiosInstance);
11
11
 
@@ -35,13 +35,11 @@ describe('Test useValueEvaluation', () => {
35
35
  it('test unitId not null request success', async () => {
36
36
  mock.onGet(API.VALUE_EVALUATIONS()).replyOnce(200, {
37
37
  results: [],
38
- next: 'link',
39
38
  });
40
- mock.onGet(API.VALUE_EVALUATIONS()).replyOnce(200, {
41
- results: [],
42
- });
43
- renderHook(() => useValueEvaluations(1), {
44
- wrapper,
39
+ await act(async () => {
40
+ renderHook(() => useValueEvaluations(1), {
41
+ wrapper,
42
+ });
45
43
  });
46
44
  expect(mock.history.get.length).toBe(1);
47
45
  });
@@ -50,8 +48,10 @@ describe('Test useValueEvaluation', () => {
50
48
  mock.onGet(API.VALUE_EVALUATIONS()).replyOnce(400, {
51
49
  results: [],
52
50
  });
53
- renderHook(() => useValueEvaluations(1), {
54
- wrapper,
51
+ await act(async () => {
52
+ renderHook(() => useValueEvaluations(1), {
53
+ wrapper,
54
+ });
55
55
  });
56
56
  expect(mock.history.get.length).toBe(1);
57
57
  });
@@ -1,6 +1,6 @@
1
1
  import moment from 'moment';
2
2
 
3
- import { getConfigGlobalState, setConfigGlobalState } from './states';
3
+ import { setConfigGlobalState } from './states';
4
4
 
5
5
  const isNewer = (valueTime, valueMicro, currentTime, currentMicro) => {
6
6
  if (valueTime?.isAfter(currentTime)) {
@@ -40,21 +40,42 @@ export const updateGlobalValues = (data) => {
40
40
  });
41
41
  };
42
42
 
43
+ let pending = {};
44
+ let scheduled = false;
45
+
43
46
  export const updateGlobalValue = (configId, value) => {
44
- const configValues = getConfigGlobalState('configValues');
45
- const currentValue = configValues[configId];
46
- if (
47
- !isNewer(
48
- value.last_updated,
49
- value.last_updated_micro,
50
- currentValue?.last_updated || moment(0),
51
- currentValue?.last_updated_micro || 0
52
- )
53
- ) {
54
- return;
47
+ pending[configId] = value;
48
+
49
+ if (!scheduled) {
50
+ scheduled = true;
51
+
52
+ requestAnimationFrame(() => {
53
+ setConfigGlobalState('configValues', (prev) => {
54
+ let changed = false;
55
+ const next = { ...prev };
56
+
57
+ Object.keys(pending).forEach((id) => {
58
+ const val = pending[id];
59
+ const cur = prev[id];
60
+
61
+ if (
62
+ isNewer(
63
+ val.last_updated,
64
+ val.last_updated_micro,
65
+ cur?.last_updated || moment(0),
66
+ cur?.last_updated_micro || 0
67
+ )
68
+ ) {
69
+ next[id] = val;
70
+ changed = true;
71
+ }
72
+ });
73
+
74
+ pending = {};
75
+ scheduled = false;
76
+
77
+ return changed ? next : prev;
78
+ });
79
+ });
55
80
  }
56
- setConfigGlobalState('configValues', {
57
- ...configValues,
58
- [configId]: value,
59
- });
60
81
  };
package/src/iot/mqtt.js CHANGED
@@ -1,6 +1,6 @@
1
+ import { Buffer } from 'buffer';
1
2
  import { getObject, storeObject } from '../utils/Storage';
2
3
  import { getLastUpdated, updateGlobalValue } from './UpdateStates';
3
- import { Buffer } from 'buffer';
4
4
 
5
5
  const struct = require('python-struct');
6
6
 
@@ -193,6 +193,19 @@ const convert_ai = (bytes_str, config_data = null) => {
193
193
  );
194
194
  };
195
195
 
196
+ const power_factor_4q_fp_pf = (value) => {
197
+ let pfVal;
198
+ if (value > 1) {
199
+ pfVal = 2 - value;
200
+ } else if (value < -1) {
201
+ pfVal = -2 - value;
202
+ } else {
203
+ pfVal = value;
204
+ }
205
+
206
+ return pfVal;
207
+ };
208
+
196
209
  const mappingTransformer = {
197
210
  int_all: int_all,
198
211
  float_abcd: float_abcd,
@@ -224,6 +237,10 @@ const mappingTransformer = {
224
237
  int_64_cdab: int_64_cdab,
225
238
  };
226
239
 
240
+ const mappingValueConverter = {
241
+ power_factor_4q_fp_pf: power_factor_4q_fp_pf,
242
+ };
243
+
227
244
  const extract_config_value = (register, value_bytes) => {
228
245
  const transformer = mappingTransformer[register?.transformer];
229
246
  if (!transformer) {
@@ -303,6 +320,13 @@ const get_offset_sample_value = async (value, config) => {
303
320
  return value;
304
321
  };
305
322
 
323
+ const getValueConverter = (value, config) => {
324
+ const converter = mappingValueConverter[config?.value_converter];
325
+ return converter
326
+ ? Number(get_decimal_value(converter(value), config))
327
+ : value;
328
+ };
329
+
306
330
  const check_filter = (value, config) => {
307
331
  if (!config?.active_filter) {
308
332
  return false;
@@ -333,6 +357,7 @@ const updateGlobalValueMqtt = async (config, value, time) => {
333
357
 
334
358
  value = get_scale_value(value, config);
335
359
  value = await get_offset_sample_value(value, config);
360
+ value = getValueConverter(value, config);
336
361
 
337
362
  if (check_filter(value, config)) {
338
363
  return;
@@ -11,7 +11,7 @@ import { SCContext } from '../context';
11
11
  import { Action } from '../context/actionType';
12
12
  import { useTranslations } from '../hooks/Common/useTranslations';
13
13
  import { unwatchAllConfigs } from '../iot/Monitor';
14
- import ActivityLogScreen from '../screens/ActivityLog';
14
+ import ActivityLog from '../screens/ActivityLog';
15
15
  import Route from '../utils/Route';
16
16
  import { screenOptions } from './utils';
17
17
 
@@ -350,7 +350,7 @@ export const UnitStack = memo((props) => {
350
350
  />
351
351
  <Stack.Screen
352
352
  name={Route.ActivityLog}
353
- component={ActivityLogScreen}
353
+ component={ActivityLog}
354
354
  options={{
355
355
  headerShown: false,
356
356
  }}
@@ -1,10 +1,14 @@
1
- import React from 'react';
2
- import { View } from 'react-native';
1
+ import React, { useState, useCallback } from 'react';
2
+ import { View, TouchableOpacity, Modal } from 'react-native';
3
3
  import moment from 'moment';
4
4
  import Text from '../../commons/Text';
5
5
  import t from '../../hooks/Common/useTranslations';
6
6
  import styles from './styles/itemLogStyles';
7
- import { AUTOMATE_TYPE, ACTIVITY_LOG_TYPES } from '../../configs/Constants';
7
+ import {
8
+ AUTOMATE_TYPE,
9
+ ACTIVITY_LOG_TYPES,
10
+ AccessibilityLabel,
11
+ } from '../../configs/Constants';
8
12
 
9
13
  const DetailEmergencyEventLog = ({ item }) => (
10
14
  <Text style={styles.text}>
@@ -20,34 +24,83 @@ const DetailEmergencyEventLog = ({ item }) => (
20
24
  );
21
25
 
22
26
  const DetailLog = ({ item }) => {
23
- switch (item.content_code) {
27
+ const [showModal, setShowModal] = useState(false);
28
+ const { name, username, created_at, message, content_code, action_name } =
29
+ item;
30
+
31
+ const handlePress = useCallback(() => {
32
+ if (message) {
33
+ setShowModal(true);
34
+ }
35
+ }, [message]);
36
+
37
+ const handleClose = useCallback(() => {
38
+ setShowModal(false);
39
+ }, []);
40
+
41
+ switch (content_code) {
24
42
  case ACTIVITY_LOG_TYPES.AUTO_ACTIVATED:
25
43
  return <Text style={styles.text}>{t('auto_activated')}</Text>;
26
44
  case ACTIVITY_LOG_TYPES.ACTIVATED_BY:
27
45
  return (
28
46
  <Text style={styles.text}>
29
- {item.action_name
30
- ? `${item.action_name} ${t('by')} `
31
- : `${t('activated_by')} `}
32
- <Text style={styles.name}>{item.name || item.username}</Text>
47
+ {action_name ? `${action_name} ${t('by')} ` : `${t('activated_by')} `}
48
+ <Text style={styles.name}>{name || username}</Text>
33
49
  </Text>
34
50
  );
35
51
  case ACTIVITY_LOG_TYPES.SCRIPT_UPDATED_BY:
36
52
  return (
37
53
  <Text style={styles.text}>
38
54
  {`${t('script_updated_by')} `}
39
- <Text style={styles.name}>{item.name || item.username}</Text>
55
+ <Text style={styles.name}>{name || username}</Text>
40
56
  </Text>
41
57
  );
42
58
  default:
43
59
  return (
44
- <Text style={styles.text}>
45
- {item.action_name
46
- ? `${item.action_name} ${t('by')} `
47
- : `${t('activated_by')} `}
48
- <Text style={styles.name}>{item.name || item.username}</Text>
49
- <Text style={styles.text}>{'\n' + item.message}</Text>
50
- </Text>
60
+ <>
61
+ <TouchableOpacity onPress={handlePress} activeOpacity={0.7}>
62
+ <Text style={styles.text}>
63
+ {action_name
64
+ ? `${action_name} ${t('by')} `
65
+ : `${t('activated_by')} `}
66
+ <Text style={styles.name}>{name || username}</Text>
67
+ </Text>
68
+ </TouchableOpacity>
69
+ <Modal
70
+ visible={showModal}
71
+ transparent
72
+ animationType="fade"
73
+ onRequestClose={handleClose}
74
+ accessibilityLabel={AccessibilityLabel.MODAL_BUTTON_POPUP}
75
+ >
76
+ <TouchableOpacity
77
+ style={styles.modalOverlay}
78
+ activeOpacity={1}
79
+ onPress={handleClose}
80
+ accessibilityLabel={AccessibilityLabel.TOUCHABLE_OPACITY}
81
+ >
82
+ <View style={styles.modalContent}>
83
+ <Text style={styles.modalTitle}>{t('detail')}</Text>
84
+ <Text style={styles.modalMessage}>
85
+ {action_name
86
+ ? `${action_name} ${t('by')} `
87
+ : `${t('activated_by')} `}
88
+ <Text style={styles.name}>{name || username}</Text>
89
+ </Text>
90
+ <Text style={styles.modalMessage}>{message}</Text>
91
+ <Text style={styles.modalMessage}>
92
+ {moment(created_at).format('DD/MM/YYYY HH:mm:ss')}
93
+ </Text>
94
+ <TouchableOpacity
95
+ style={styles.modalCloseButton}
96
+ onPress={handleClose}
97
+ >
98
+ <Text style={styles.modalCloseText}>{t('close')}</Text>
99
+ </TouchableOpacity>
100
+ </View>
101
+ </TouchableOpacity>
102
+ </Modal>
103
+ </>
51
104
  );
52
105
  }
53
106
  };