@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.
Files changed (60) hide show
  1. package/package.json +4 -4
  2. package/src/commons/ActionGroup/CurtainButtonTemplate.js +32 -21
  3. package/src/commons/ActionGroup/NumberUpDownActionTemplate.js +8 -6
  4. package/src/commons/ActionGroup/OnOffTemplate/index.js +11 -3
  5. package/src/commons/ActionGroup/OneBigButtonTemplate.js +10 -7
  6. package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +5 -2
  7. package/src/commons/ActionGroup/StatesGridActionTemplate.js +7 -3
  8. package/src/commons/ActionGroup/ThreeButtonTemplate.js +33 -24
  9. package/src/commons/ActionGroup/__test__/OnOffTemplate.test.js +18 -6
  10. package/src/commons/ActionGroup/__test__/OneBigButtonTemplate.test.js +9 -1
  11. package/src/commons/ActionGroup/__test__/OptionsDropdownTemplate.test.js +25 -13
  12. package/src/commons/ActionGroup/__test__/index.test.js +48 -14
  13. package/src/commons/Device/HorizontalBarChart.js +8 -2
  14. package/src/commons/Device/ItemDevice.js +8 -5
  15. package/src/commons/Device/LinearChart.js +1 -1
  16. package/src/commons/EmergencyButton/AlertSendConfirm.js +2 -2
  17. package/src/commons/EmergencyButton/AlertSent.js +2 -2
  18. package/src/commons/ImagePicker/__test__/ImagePicker.test.js +24 -3
  19. package/src/commons/MediaPlayerDetail/index.js +1 -0
  20. package/src/commons/SubUnit/Favorites/index.js +2 -0
  21. package/src/commons/SubUnit/ShortDetail.js +7 -1
  22. package/src/configs/API.js +2 -4
  23. package/src/configs/Constants.js +5 -0
  24. package/src/iot/RemoteControl/Internet.js +8 -1
  25. package/src/iot/RemoteControl/index.js +4 -2
  26. package/src/screens/AddCommon/SelectSubUnit.js +6 -0
  27. package/src/screens/AddNewGateway/__test__/SetupGateway.test.js +52 -0
  28. package/src/screens/AllCamera/index.js +76 -44
  29. package/src/screens/Device/EditDevice/__test__/EditDevice.test.js +2 -2
  30. package/src/screens/Device/EditDevice/index.js +2 -2
  31. package/src/screens/Device/__test__/detail.test.js +18 -11
  32. package/src/screens/Device/components/SensorConnectStatusViewHeader.js +2 -2
  33. package/src/screens/Device/components/SensorDisplayItem.js +2 -2
  34. package/src/screens/Device/detail.js +58 -20
  35. package/src/screens/Notification/__test__/NotificationItem.test.js +186 -14
  36. package/src/screens/Notification/components/NotificationItem.js +147 -3
  37. package/src/screens/Notification/index.js +4 -1
  38. package/src/screens/Notification/styles/NotificationItemStyles.js +3 -3
  39. package/src/screens/Sharing/SelectUser.js +17 -10
  40. package/src/screens/Sharing/__test__/SelectUser.test.js +73 -0
  41. package/src/screens/SubUnit/AddSubUnit.js +4 -1
  42. package/src/screens/SubUnit/__test__/AddSubUnit.test.js +148 -0
  43. package/src/screens/Unit/Detail.js +28 -4
  44. package/src/screens/Unit/ManageUnit.js +88 -32
  45. package/src/screens/Unit/ManageUnitStyles.js +20 -0
  46. package/src/screens/Unit/SmartAccount.js +6 -2
  47. package/src/screens/Unit/Summaries.js +2 -2
  48. package/src/screens/Unit/__test__/CheckSendEmail.test.js +10 -0
  49. package/src/screens/Unit/__test__/Detail.test.js +53 -0
  50. package/src/screens/Unit/__test__/ManageUnit.test.js +69 -0
  51. package/src/screens/Unit/__test__/SmartAccount.test.js +37 -8
  52. package/src/screens/Unit/components/__test__/SharedUnit.test.js +21 -2
  53. package/src/screens/Unit/hook/useStateAlertRemove.js +1 -1
  54. package/src/screens/UnitSummary/__test__/index.test.js +3 -3
  55. package/src/screens/UnitSummary/components/RunningDevices/__test__/index.test.js +2 -2
  56. package/src/screens/UnitSummary/index.js +52 -9
  57. package/src/utils/Apis/axios.js +7 -2
  58. package/src/utils/I18n/translations/en.json +10 -1
  59. package/src/utils/I18n/translations/vi.json +10 -1
  60. package/src/{screens/Notification → utils}/Monitor.js +1 -1
@@ -1,4 +1,10 @@
1
- import React, { useEffect, useMemo, useRef, useState } from 'react';
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
2
8
  import { View, Text, TouchableOpacity, Platform, Image } from 'react-native';
3
9
  import { useRoute, useNavigation } from '@react-navigation/native';
4
10
  import { chunk } from 'lodash';
@@ -28,36 +34,51 @@ const AllCamera = () => {
28
34
  const [isFullScreen, setIsFullScreen] = useState(false);
29
35
  const [dataFullScreen, setDataFullScreen] = useState();
30
36
 
31
- const handleFullScreen = (data) => {
32
- setIsFullScreen(!isFullScreen);
33
- setDataFullScreen(data);
34
- };
37
+ const handleFullScreen = useCallback(
38
+ (data) => {
39
+ setIsFullScreen(!isFullScreen);
40
+ setDataFullScreen(data);
41
+ },
42
+ [isFullScreen]
43
+ );
35
44
 
36
- const onPressAmount = (value) => () => {
37
- setAmount(value);
38
- value === 1 && setActiveCamera(arrCameras[0]);
39
- const dataTemp = [...arrCameras];
40
- setData(value === 1 ? dataTemp : chunk(dataTemp, value));
41
- };
45
+ const onPressAmount = useCallback(
46
+ (value) => () => {
47
+ setAmount(value);
48
+ value === 1 && setActiveCamera(arrCameras[0]);
49
+ const dataTemp = [...arrCameras];
50
+ setData(value === 1 ? dataTemp : chunk(dataTemp, value));
51
+ setCurrentPage(1);
52
+ },
53
+ [arrCameras]
54
+ );
42
55
 
43
- const onPressNext = () =>
56
+ const onPressNext = useCallback(() => {
44
57
  currentPage !== data.length && carouselRef?.current?.snapToNext();
58
+ }, [currentPage, data.length]);
45
59
 
46
- const onPressPrev = () =>
60
+ const onPressPrev = useCallback(() => {
47
61
  currentPage !== 1 && carouselRef?.current?.snapToPrev();
62
+ }, [currentPage]);
48
63
 
49
- const onSnapToItem = (index) => {
50
- setActiveCamera(data[index]);
51
- setCurrentPage(index + 1);
52
- };
64
+ const onSnapToItem = useCallback(
65
+ (index) => {
66
+ setActiveCamera(data[index]);
67
+ setCurrentPage(index + 1);
68
+ },
69
+ [data]
70
+ );
53
71
 
54
72
  const onClose = () => {
55
73
  setIsFullScreen(false);
56
74
  };
57
75
 
58
- const goToPlayBack = (item, thumbnail) => () => {
59
- navigate(Routes.PlaybackCamera, { item, thumbnail });
60
- };
76
+ const goToPlayBack = useCallback(
77
+ (item, thumbnail) => () => {
78
+ navigate(Routes.PlaybackCamera, { item, thumbnail });
79
+ },
80
+ [navigate]
81
+ );
61
82
 
62
83
  const CameraItem = ({ item, width, height }) => {
63
84
  return (
@@ -82,7 +103,7 @@ const AllCamera = () => {
82
103
  );
83
104
  };
84
105
 
85
- const renderItem = ({ item, index }) => {
106
+ const renderItem = ({ item }) => {
86
107
  return (
87
108
  <View style={styles.wrapItem}>
88
109
  {amount === 1 ? (
@@ -93,13 +114,13 @@ const AllCamera = () => {
93
114
  <CameraItem
94
115
  key={item?.id}
95
116
  item={item[0]}
96
- width={188}
117
+ width={Constants.width / 2}
97
118
  height={112}
98
119
  />
99
120
  <CameraItem
100
121
  key={item?.id}
101
122
  item={item[1]}
102
- width={188}
123
+ width={Constants.width / 2}
103
124
  height={112}
104
125
  />
105
126
  </View>
@@ -107,13 +128,13 @@ const AllCamera = () => {
107
128
  <CameraItem
108
129
  key={item?.id}
109
130
  item={item[2]}
110
- width={188}
131
+ width={Constants.width / 2}
111
132
  height={112}
112
133
  />
113
134
  <CameraItem
114
135
  key={item?.id}
115
136
  item={item[3]}
116
- width={188}
137
+ width={Constants.width / 2}
117
138
  height={112}
118
139
  />
119
140
  </View>
@@ -138,19 +159,23 @@ const AllCamera = () => {
138
159
 
139
160
  const renderCarousel = useMemo(() => {
140
161
  return (
141
- <Carousel
142
- ref={carouselRef}
143
- data={data}
144
- renderItem={renderItem}
145
- sliderWidth={Constants.width}
146
- itemWidth={Constants.width}
147
- inactiveSlideScale={1}
148
- onSnapToItem={onSnapToItem}
149
- inactiveSlideOpacity={1}
150
- extraData={data}
151
- loop={false}
152
- isForceIndex={Platform.OS === 'android'}
153
- />
162
+ <View style={styles.wrapCarousel}>
163
+ <Carousel
164
+ ref={carouselRef}
165
+ data={data}
166
+ renderItem={renderItem}
167
+ sliderWidth={Constants.width}
168
+ itemWidth={Constants.width}
169
+ inactiveSlideScale={1}
170
+ onSnapToItem={onSnapToItem}
171
+ inactiveSlideOpacity={1}
172
+ extraData={data}
173
+ loop={false}
174
+ lockScrollWhileSnapping
175
+ isForceIndex={Platform.OS === 'android'}
176
+ removeClippedSubviews={false}
177
+ />
178
+ </View>
154
179
  );
155
180
  // eslint-disable-next-line react-hooks/exhaustive-deps
156
181
  }, [data]);
@@ -182,6 +207,17 @@ const AllCamera = () => {
182
207
  // eslint-disable-next-line react-hooks/exhaustive-deps
183
208
  }, [amount]);
184
209
 
210
+ const renderName = useMemo(
211
+ () => (
212
+ <Text style={styles.cameraName}>
213
+ {amount === 1
214
+ ? activeCamera?.configuration?.name
215
+ : `${currentPage}/${data.length}`}
216
+ </Text>
217
+ ),
218
+ [activeCamera?.configuration?.name, amount, currentPage, data.length]
219
+ );
220
+
185
221
  useEffect(() => {
186
222
  const to = setTimeout(() => {
187
223
  carouselRef?.current?.snapToItem();
@@ -203,16 +239,12 @@ const AllCamera = () => {
203
239
  <TouchableOpacity style={styles.buttonNext} onPress={onPressPrev}>
204
240
  <Image source={Images.arrowLeft} />
205
241
  </TouchableOpacity>
206
- <Text numberOfLines={1} style={styles.cameraName}>
207
- {amount === 1
208
- ? activeCamera?.configuration?.name
209
- : `${currentPage}/${data.length}`}
210
- </Text>
242
+ {renderName}
211
243
  <TouchableOpacity style={styles.buttonNext} onPress={onPressNext}>
212
244
  <Image source={Images.arrowLeft} style={styles.arrowRight} />
213
245
  </TouchableOpacity>
214
246
  </View>
215
- <View style={styles.wrapCarousel}>{renderCarousel}</View>
247
+ {renderCarousel}
216
248
  {renderAmount}
217
249
  </View>
218
250
  </View>
@@ -90,7 +90,7 @@ describe('Test EditDevice', () => {
90
90
  await act(async () => {
91
91
  await alertAction.props.rightButtonClick();
92
92
  });
93
- expect(axios.patch).toHaveBeenCalledWith(API.SENSOR.RENAME_SENSOR(1), {
93
+ expect(axios.patch).toHaveBeenCalledWith(API.SENSOR.SENSOR_DETAIL(1), {
94
94
  name: 'new_name',
95
95
  });
96
96
  expect(alertAction.props.visible).toBeFalsy();
@@ -119,7 +119,7 @@ describe('Test EditDevice', () => {
119
119
  await act(async () => {
120
120
  await alertAction.props.rightButtonClick();
121
121
  });
122
- expect(axios.delete).toHaveBeenCalledWith(API.SENSOR.REMOVE_SENSOR(1));
122
+ expect(axios.delete).toHaveBeenCalledWith(API.SENSOR.SENSOR_DETAIL(1));
123
123
  expect(alertAction.props.visible).toBeFalsy();
124
124
  expect(mockPop).toHaveBeenCalled();
125
125
  });
@@ -29,7 +29,7 @@ const EditDevice = memo(() => {
29
29
  useEditDevice();
30
30
  const renameSensor = useCallback(async () => {
31
31
  const { success, data } = await axiosPatch(
32
- API.SENSOR.RENAME_SENSOR(sensor?.id),
32
+ API.SENSOR.SENSOR_DETAIL(sensor?.id),
33
33
  {
34
34
  name: inputName,
35
35
  }
@@ -53,7 +53,7 @@ const EditDevice = memo(() => {
53
53
 
54
54
  const deleteSensor = useCallback(async () => {
55
55
  hideAlertAction();
56
- const { success } = await axiosDelete(API.SENSOR.REMOVE_SENSOR(sensor?.id));
56
+ const { success } = await axiosDelete(API.SENSOR.SENSOR_DETAIL(sensor?.id));
57
57
 
58
58
  if (success) {
59
59
  navigation.pop(2);
@@ -93,7 +93,7 @@ describe('test DeviceDetail', () => {
93
93
  beforeEach(() => {
94
94
  route = {
95
95
  params: {
96
- unit: {
96
+ unitData: {
97
97
  id: 1,
98
98
  name: 'Unit name',
99
99
  address: '298 Dien Bien Phu',
@@ -105,10 +105,11 @@ describe('test DeviceDetail', () => {
105
105
  id: 2,
106
106
  name: 'Station name',
107
107
  },
108
- sensor: {
108
+ sensorData: {
109
109
  id: 1,
110
110
  is_managed_by_backend: true,
111
111
  station: { id: 2, name: 'Station name' },
112
+ name: 'Sensor name',
112
113
  },
113
114
  title: 'Button',
114
115
  },
@@ -252,7 +253,9 @@ describe('test DeviceDetail', () => {
252
253
  });
253
254
  expect(axios.post).toHaveBeenCalledWith(API.SENSOR.QUICK_ACTION(1), {
254
255
  key: responseDisplay.data.items[1].configuration.configuration.action1,
256
+ data: null,
255
257
  source: 'internet',
258
+ action_name: undefined,
256
259
  });
257
260
 
258
261
  const button2 = instance.find(
@@ -263,7 +266,9 @@ describe('test DeviceDetail', () => {
263
266
  });
264
267
  expect(axios.post).toHaveBeenCalledWith(API.SENSOR.QUICK_ACTION(1), {
265
268
  key: responseDisplay.data.items[1].configuration.configuration.action2,
269
+ data: null,
266
270
  source: 'internet',
271
+ action_name: undefined,
267
272
  });
268
273
 
269
274
  const button3 = instance.find(
@@ -274,7 +279,9 @@ describe('test DeviceDetail', () => {
274
279
  });
275
280
  expect(axios.post).toHaveBeenCalledWith(API.SENSOR.QUICK_ACTION(1), {
276
281
  key: responseDisplay.data.items[1].configuration.configuration.action3,
282
+ data: null,
277
283
  source: 'internet',
284
+ action_name: undefined,
278
285
  });
279
286
  });
280
287
 
@@ -628,7 +635,7 @@ describe('test DeviceDetail', () => {
628
635
  });
629
636
 
630
637
  test('not fetch value if not managed by backend', async () => {
631
- route.params.sensor.is_managed_by_backend = false;
638
+ route.params.sensorData.is_managed_by_backend = false;
632
639
 
633
640
  await act(async () => {
634
641
  tree = await create(wrapComponent(account, route));
@@ -638,7 +645,7 @@ describe('test DeviceDetail', () => {
638
645
  });
639
646
 
640
647
  test('render CurrentRainSensor but is other device', async () => {
641
- route.params.sensor.is_other_device = true;
648
+ route.params.sensorData.is_other_device = true;
642
649
  route.params.isGGHomeConnected = true;
643
650
 
644
651
  const responseDisplay = {
@@ -708,9 +715,9 @@ describe('test DeviceDetail', () => {
708
715
  });
709
716
 
710
717
  test('Add device to Favourites', async () => {
711
- const unit_id = route.params.unit.id;
718
+ const unit_id = route.params.unitData.id;
712
719
  const station_id = route.params.station.id;
713
- const sensor = route.params.sensor;
720
+ const sensor = route.params.sensorData;
714
721
  sensor.is_favourite = false;
715
722
 
716
723
  await act(async () => {
@@ -736,9 +743,9 @@ describe('test DeviceDetail', () => {
736
743
  });
737
744
 
738
745
  test('Remove device from Favourites', async () => {
739
- const unit_id = route.params.unit.id;
746
+ const unit_id = route.params.unitData.id;
740
747
  const station_id = route.params.station.id;
741
- const sensor = route.params.sensor;
748
+ const sensor = route.params.sensorData;
742
749
  sensor.is_favourite = true;
743
750
 
744
751
  await act(async () => {
@@ -810,12 +817,12 @@ describe('test DeviceDetail', () => {
810
817
  await menu.props.onItemClick(gotoActivityLog);
811
818
  });
812
819
  expect(mockedNavigate).toHaveBeenCalledWith(Routes.ActivityLog, {
813
- id: route.params.sensor.id,
820
+ id: route.params.sensorData.id,
814
821
  type: 'action',
815
- share: route.params.unit,
822
+ share: route.params.unitData,
816
823
  filterEnabled: {
817
824
  date: true,
818
- user: Boolean(route.params.unit.id),
825
+ user: Boolean(route.params.unitData.id),
819
826
  },
820
827
  });
821
828
  });
@@ -3,7 +3,7 @@ import { ConnectedViewHeader, DisconnectedView } from '../../../commons/Device';
3
3
  import { DEVICE_TYPE } from '../../../configs/Constants';
4
4
 
5
5
  export const SensorConnectStatusViewHeader = (props) => {
6
- if (!!props.sensor && !props.sensor.is_other_device) {
6
+ if (!!props.sensor && !props.sensor?.is_other_device) {
7
7
  if (props.connected) {
8
8
  return (
9
9
  <>
@@ -31,7 +31,7 @@ export const SensorConnectStatusViewHeader = (props) => {
31
31
  return <DisconnectedView sensor={props.sensor} />;
32
32
  }
33
33
  } else {
34
- if (props.sensor.device_type === DEVICE_TYPE.LG_THINQ) {
34
+ if (props.sensor?.device_type === DEVICE_TYPE.LG_THINQ) {
35
35
  return (
36
36
  <>
37
37
  <ConnectedViewHeader lastUpdated={props.lastUpdated} />
@@ -40,8 +40,8 @@ export const SensorDisplayItem = ({
40
40
  const t = useTranslations();
41
41
  const userId = useSCContextSelector((state) => state.auth.account.user.id);
42
42
  const doAction = useCallback(
43
- (action, data) => {
44
- sendRemoteCommand(sensor, action, data, userId);
43
+ (action, data, actionName) => {
44
+ sendRemoteCommand(sensor, action, data, userId, actionName);
45
45
  },
46
46
  [sensor, userId]
47
47
  );
@@ -70,9 +70,13 @@ const DeviceDetail = ({ route }) => {
70
70
  // eslint-disable-next-line no-unused-vars
71
71
  const [configValues, setConfigValues] = useConfigGlobalState('configValues');
72
72
 
73
- const { unit, sensor, isGGHomeConnected, station } = route.params;
74
- const [isFavourite, setIsFavourite] = useState(sensor.is_favourite);
75
- const { isOwner } = useIsOwnerOfUnit(unit.user_id);
73
+ const { unitData, unitId, sensorData, sensorId, isGGHomeConnected } =
74
+ route.params;
75
+ const [unit, setUnit] = useState(unitData || { id: unitId });
76
+ const [sensor, setSensor] = useState(sensorData || { id: sensorId });
77
+ const [station, setStation] = useState(sensor?.station);
78
+ const [isFavourite, setIsFavourite] = useState(sensor?.is_favourite);
79
+ const { isOwner } = useIsOwnerOfUnit(unit?.user_id);
76
80
  const [sensorName, setSensorName] = useState(sensor?.name);
77
81
  const [lockShowing, acquireLockShowing, releaseLockShowing] = useBoolean();
78
82
  const [showWindDirection, setShowWindDirection] = useState(false);
@@ -108,7 +112,7 @@ const DeviceDetail = ({ route }) => {
108
112
 
109
113
  const addToFavorites = useCallback(async () => {
110
114
  const { success } = await axiosPost(
111
- API.SENSOR.ADD_TO_FAVOURITES(unit.id, sensor.station.id, sensor.id)
115
+ API.SENSOR.ADD_TO_FAVOURITES(unit?.id, sensor?.station?.id, sensor?.id)
112
116
  );
113
117
  if (success) {
114
118
  setIsFavourite(true);
@@ -117,7 +121,11 @@ const DeviceDetail = ({ route }) => {
117
121
 
118
122
  const removeFromFavorites = useCallback(async () => {
119
123
  const { success } = await axiosPost(
120
- API.SENSOR.REMOVE_FROM_FAVOURITES(unit.id, sensor.station.id, sensor.id)
124
+ API.SENSOR.REMOVE_FROM_FAVOURITES(
125
+ unit?.id,
126
+ sensor?.station.id,
127
+ sensor?.id
128
+ )
121
129
  );
122
130
  if (success) {
123
131
  setIsFavourite(false);
@@ -129,9 +137,39 @@ const DeviceDetail = ({ route }) => {
129
137
  );
130
138
 
131
139
  const canManageSubUnit = useMemo(() => {
132
- return currentUserId === unit.user_id;
140
+ return currentUserId === unit?.user_id;
133
141
  }, [currentUserId, unit]);
134
142
 
143
+ const fetchUnitDetail = useCallback(async () => {
144
+ const { success, data } = await axiosGet(API.UNIT.UNIT_DETAIL(unit?.id));
145
+ if (success) {
146
+ setUnit(data);
147
+ }
148
+ }, [unit?.id]);
149
+
150
+ useEffect(() => {
151
+ if (!unitData && unitId) {
152
+ fetchUnitDetail();
153
+ }
154
+ }, [fetchUnitDetail, unitData, unitId]);
155
+
156
+ const fetchSensorDetail = useCallback(async () => {
157
+ const { success, data } = await axiosGet(
158
+ API.SENSOR.SENSOR_DETAIL(sensor?.id)
159
+ );
160
+ if (success) {
161
+ setSensor(data);
162
+ setSensorName(data.name);
163
+ setStation(data.station);
164
+ }
165
+ }, [sensor?.id]);
166
+
167
+ useEffect(() => {
168
+ if (!sensorData && sensorId) {
169
+ fetchSensorDetail();
170
+ }
171
+ }, [fetchSensorDetail, sensorData, sensorId]);
172
+
135
173
  const fetchDataDeviceDetail = useCallback(async () => {
136
174
  if (!token) {
137
175
  return;
@@ -141,7 +179,7 @@ const DeviceDetail = ({ route }) => {
141
179
  }
142
180
 
143
181
  const displayResult = await axiosGet(
144
- API.SENSOR.DISPLAY(sensor.id),
182
+ API.SENSOR.DISPLAY(sensor?.id),
145
183
  {},
146
184
  true
147
185
  );
@@ -168,7 +206,7 @@ const DeviceDetail = ({ route }) => {
168
206
  setLoading((preState) => ({ ...preState, displayTemplate: false }));
169
207
 
170
208
  const controlResult = await axiosGet(
171
- API.SENSOR.REMOTE_CONTROL_OPTIONS(sensor.id),
209
+ API.SENSOR.REMOTE_CONTROL_OPTIONS(sensor?.id),
172
210
  {},
173
211
  true
174
212
  );
@@ -212,12 +250,12 @@ const DeviceDetail = ({ route }) => {
212
250
  menuItems.push({
213
251
  route: Routes.ActivityLog,
214
252
  data: {
215
- id: sensor.id,
253
+ id: sensor?.id,
216
254
  type: 'action',
217
255
  share: unit,
218
256
  filterEnabled: {
219
257
  date: true,
220
- user: Boolean(unit.id),
258
+ user: Boolean(unit?.id),
221
259
  },
222
260
  },
223
261
  text: t('activity_log'),
@@ -352,7 +390,7 @@ const DeviceDetail = ({ route }) => {
352
390
 
353
391
  const fetchValues = async () => {
354
392
  const { success, data } = await axiosGet(
355
- API.SENSOR.DISPLAY_VALUES_V2(sensor.id),
393
+ API.SENSOR.DISPLAY_VALUES_V2(sensor?.id),
356
394
  {
357
395
  params: params,
358
396
  }
@@ -365,7 +403,7 @@ const DeviceDetail = ({ route }) => {
365
403
  }
366
404
  setLoading((preState) => ({ ...preState, isConnected: false }));
367
405
  };
368
- if (sensor.is_managed_by_backend && !sensor.is_other_device) {
406
+ if (sensor?.is_managed_by_backend && !sensor?.is_other_device) {
369
407
  const updateInterval = setInterval(() => fetchValues(), 5000);
370
408
  fetchValues();
371
409
  return () => clearInterval(updateInterval);
@@ -405,12 +443,12 @@ const DeviceDetail = ({ route }) => {
405
443
  ).length > 0;
406
444
 
407
445
  const onSetupContacts = useCallback(() => {
408
- const group = unit.group;
446
+ const group = unit?.group;
409
447
  navigation.navigate(Routes.EmergencyContactsStack, {
410
448
  screen: Routes.EmergencyContactsList,
411
- params: { unitId: unit.id, group },
449
+ params: { unitId: unit?.id, group },
412
450
  });
413
- }, [navigation, unit.group, unit.id]);
451
+ }, [navigation, unit?.group, unit?.id]);
414
452
 
415
453
  // replace isConnected=True to see template
416
454
  const renderSensorConnected = () => {
@@ -448,12 +486,12 @@ const DeviceDetail = ({ route }) => {
448
486
 
449
487
  const getDataFromLocal = async () => {
450
488
  const displayData = await getLocalData(
451
- `@CACHE_REQUEST_${API.SENSOR.DISPLAY(sensor.id)}`
489
+ `@CACHE_REQUEST_${API.SENSOR.DISPLAY(sensor?.id)}`
452
490
  );
453
491
  displayData && setDisplay(JSON.parse(displayData));
454
492
 
455
493
  const controlOptionData = await getLocalData(
456
- `@CACHE_REQUEST_${API.SENSOR.REMOTE_CONTROL_OPTIONS(sensor.id)}`
494
+ `@CACHE_REQUEST_${API.SENSOR.REMOTE_CONTROL_OPTIONS(sensor?.id)}`
457
495
  );
458
496
  controlOptionData && setControlOptions(JSON.parse(controlOptionData));
459
497
  setLoading((preState) => ({ ...preState, displayTemplate: false }));
@@ -557,7 +595,7 @@ const DeviceDetail = ({ route }) => {
557
595
  onSendNowAlert={onSendNowAlert}
558
596
  onHide={releaseLockShowing}
559
597
  unit={unit}
560
- station={sensor.station}
598
+ station={sensor?.station}
561
599
  />
562
600
 
563
601
  <AlertSent
@@ -566,7 +604,7 @@ const DeviceDetail = ({ route }) => {
566
604
  onPressMain={onViewDetails}
567
605
  onHide={releaseLockShowing}
568
606
  unit={unit}
569
- station={sensor.station}
607
+ station={sensor?.station}
570
608
  />
571
609
  </WrapHeaderScrollable>
572
610
  {isShowEmergencyResolve && (
@@ -605,7 +643,7 @@ const DeviceDetail = ({ route }) => {
605
643
  type="H4"
606
644
  style={styles.textName}
607
645
  >
608
- {unit.name} - {sensor.station.name}
646
+ {unit?.name} - {sensor?.station?.name}
609
647
  </Text>
610
648
  <IconFill
611
649
  testID={TESTID.BUTTON_POPUP_RESOLVED_ICON}