ordering-ui-react-native 0.22.91 → 0.22.93

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ordering-ui-react-native",
3
- "version": "0.22.91",
3
+ "version": "0.22.93",
4
4
  "description": "Reusable components made in react native",
5
5
  "main": "src/index.tsx",
6
6
  "author": "ordering.inc",
@@ -0,0 +1,194 @@
1
+ import React, { useState, createContext, useEffect, useContext } from 'react';
2
+ import { useApi, useSession, useEvent } from 'ordering-components/native'
3
+ import { useNetInfo } from '@react-native-community/netinfo';
4
+
5
+ import { _retrieveStoreData, _setStoreData, _removeStoreData } from '../../providers/StoreUtil'
6
+
7
+ type State = {
8
+ isNetConnected: boolean | null
9
+ isCombinedTabs: boolean | null
10
+ canSaveChangesOffline: boolean | null
11
+ actions: Array<{ [key: string]: any }>
12
+ orders: { [key: string]: any } | null
13
+ }
14
+
15
+ type Functions = {
16
+ applyOffAction: (changes: any) => any
17
+ registerOffOrder: (order: any) => any
18
+ setState: React.Dispatch<React.SetStateAction<State>>
19
+ }
20
+
21
+ const defaultState = {
22
+ isNetConnected: null,
23
+ isCombinedTabs: null,
24
+ canSaveChangesOffline: false,
25
+ actions: [],
26
+ orders: null,
27
+ }
28
+
29
+
30
+ export const OfflineActionsContext = createContext<[State, Functions]>([
31
+ defaultState,
32
+ {
33
+ applyOffAction: () => {},
34
+ registerOffOrder: () => {},
35
+ setState: () => {}
36
+ }
37
+ ]);
38
+
39
+ export const OfflineActionsProvider = (props: any) => {
40
+ const netInfo = useNetInfo()
41
+ const [ordering] = useApi()
42
+ const [{ token }] = useSession()
43
+ const [events] = useEvent()
44
+
45
+ const [state, setState] = useState<State>({
46
+ isNetConnected: netInfo.isConnected,
47
+ isCombinedTabs: false,
48
+ canSaveChangesOffline: false,
49
+ actions: [],
50
+ orders: null
51
+ })
52
+
53
+ const getStatusById = (id: number) => {
54
+ if (!id && id !== 0) return
55
+ const active = [0, 3, 4, 7, 8, 9, 13, 14, 18, 19, 20, 21]
56
+ const pending = [0, 13]
57
+ const inProgress = [3, 4, 7, 8, 9, 14, 18, 19, 20, 21]
58
+ const completed = [1, 11, 15]
59
+
60
+ const status = pending.includes(id)
61
+ ? 'pending'
62
+ : inProgress.includes(id)
63
+ ? 'inProgress'
64
+ : completed.includes(id)
65
+ ? 'completed'
66
+ : 'cancelled'
67
+
68
+ const combinedStatus = active.includes(id)
69
+ ? 'active'
70
+ : completed.includes(id)
71
+ ? 'completed'
72
+ : 'cancelled'
73
+
74
+ return state.isCombinedTabs ? combinedStatus : status
75
+ }
76
+
77
+ const applyOffAction = async (changes: any) => {
78
+ if (state.canSaveChangesOffline === false) return false
79
+
80
+ const actions = [...new Set([...state.actions, changes])]
81
+
82
+ setState(state => ({ ...state, actions }))
83
+ await _setStoreData('offline_actions_array', actions)
84
+ return true
85
+ }
86
+
87
+ const registerOffOrder = async (order: any) => {
88
+ if (!order) return
89
+
90
+ const oldStatusString = getStatusById(order?.oldStatus)
91
+ const newStatusString = getStatusById(order?.status)
92
+
93
+ const orderStatuses: any = [oldStatusString]
94
+ oldStatusString !== newStatusString && orderStatuses.push(newStatusString)
95
+
96
+ const ordersStoraged: any = {}
97
+ for (const status of orderStatuses) {
98
+ ordersStoraged[status] = await _retrieveStoreData(`${status}_orders`)
99
+
100
+ if (ordersStoraged[status]) {
101
+ if (orderStatuses.length === 1) {
102
+ ordersStoraged[status] = [
103
+ ...ordersStoraged[status].filter((_order: any) => _order.id !== order.id),
104
+ order
105
+ ].sort((a: any, b: any) => b.id - a.id)
106
+ } else if (orderStatuses.length === 2) {
107
+ if (status === oldStatusString) {
108
+ ordersStoraged[status] = ordersStoraged[status]
109
+ .filter((_order: any) => _order.id !== order.id)
110
+ .sort((a: any, b: any) => b.id - a.id)
111
+ }
112
+ if (status === newStatusString) {
113
+ ordersStoraged[status] = [
114
+ ...ordersStoraged[status],
115
+ order
116
+ ].sort((a: any, b: any) => b.id - a.id)
117
+ }
118
+ }
119
+ await _setStoreData(`${status}_orders`, ordersStoraged[status]);
120
+ } else {
121
+ ordersStoraged[status] = [order]
122
+ }
123
+ }
124
+ if (Object.keys(ordersStoraged).length) {
125
+ setState(state => ({ ...state, orders: ordersStoraged }))
126
+ }
127
+ }
128
+
129
+ const syncChanges = async (changes: any) => {
130
+ const data = []
131
+ for (const action of changes) {
132
+ const eventFunction = eventsDictiorary[action.event];
133
+ if (eventFunction) {
134
+ const id = await eventFunction(action.data);
135
+ id && data.push(id)
136
+ }
137
+ }
138
+ data.length && events.emit('offline_order_updated', data)
139
+ await _removeStoreData('offline_actions_array');
140
+ setState(state => ({ ...state, actions: [] }));
141
+ }
142
+
143
+ const actionsFromStorage = async (isConnected: boolean) => {
144
+ setState(state => ({ ...state, isNetConnected: isConnected }))
145
+ const _storedActions = await _retrieveStoreData('offline_actions_array');
146
+ const storedActions: any = [...new Set(_storedActions)]
147
+
148
+ if (isConnected && storedActions?.length) {
149
+ syncChanges(storedActions)
150
+ return
151
+ }
152
+
153
+ storedActions?.length && setState(state => ({ ...state, actions: storedActions }));
154
+ }
155
+
156
+ useEffect(() => {
157
+ if (netInfo.isConnected === null || state.canSaveChangesOffline === false) return
158
+ actionsFromStorage(netInfo.isConnected)
159
+ }, [netInfo.isConnected])
160
+
161
+ const functions: any = {
162
+ applyOffAction,
163
+ registerOffOrder,
164
+ setState
165
+ }
166
+
167
+ const updateOrderStatus = async (offlineData: any) => {
168
+ try {
169
+ const { content: { result: order, error } } = await ordering
170
+ .setAccessToken(token)
171
+ .orders(offlineData?.orderId)
172
+ .save(offlineData?.body)
173
+
174
+ return error ? null : order?.id
175
+ } catch {
176
+ return null
177
+ }
178
+ }
179
+
180
+ const eventsDictiorary: any = {
181
+ evt_off_change_order_status: updateOrderStatus
182
+ }
183
+
184
+ return (
185
+ <OfflineActionsContext.Provider value={[{ ...state, isNetConnected: netInfo.isConnected }, functions]}>
186
+ {props.children}
187
+ </OfflineActionsContext.Provider>
188
+ );
189
+ };
190
+
191
+ export const useOfflineActions = () => {
192
+ const actionsManager = useContext(OfflineActionsContext)
193
+ return actionsManager || [defaultState, {}]
194
+ }
package/src/index.tsx CHANGED
@@ -94,6 +94,7 @@ import { StoreMethods } from './providers/StoreUtil';
94
94
 
95
95
  // contexts
96
96
  import { ThemeProvider, useTheme } from './context/Theme';
97
+ import { OfflineActionsProvider, useOfflineActions } from './context/OfflineActions';
97
98
 
98
99
  // hooks
99
100
  import { DeviceOrientationMethods } from './hooks/DeviceOrientation';
@@ -190,6 +191,8 @@ export {
190
191
  // contexts
191
192
  ThemeProvider,
192
193
  useTheme,
194
+ OfflineActionsProvider,
195
+ useOfflineActions,
193
196
  // hooks
194
197
  DeviceOrientationMethods,
195
198
  //types
@@ -16,7 +16,7 @@ import { useLanguage } from 'ordering-components/native';
16
16
  import { Content, Timer, TimeField, Header, Comments, CommentsButtonGroup, TopActions } from './styles';
17
17
  import { OText, OButton, OTextarea, OIcon } from '../shared';
18
18
  import { AcceptOrRejectOrderParams } from '../../types';
19
-
19
+ import { useOfflineActions } from '../../../../../src/context/OfflineActions';
20
20
  import { orderCommentList } from '../../../../../src/utils'
21
21
 
22
22
  export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
@@ -36,6 +36,8 @@ export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
36
36
 
37
37
  const [, t] = useLanguage();
38
38
  const theme = useTheme();
39
+ const [{ isNetConnected, canSaveChangesOffline }, { applyOffAction, registerOffOrder }] = useOfflineActions()
40
+
39
41
  const scrollViewRef = useRef<any>(null);
40
42
  const viewRef = useRef<any>(null);
41
43
  const timerRef = useRef() as React.MutableRefObject<TextInput>;
@@ -201,7 +203,7 @@ export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
201
203
  }
202
204
  };
203
205
 
204
- const handleAcceptOrReject = () => {
206
+ const handleAcceptOrReject = async () => {
205
207
  handleFixTime();
206
208
  let minsToSend = min;
207
209
 
@@ -278,7 +280,27 @@ export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
278
280
  }
279
281
 
280
282
  bodyToSend.id = orderId;
281
- handleUpdateOrder?.(bodyToSend.status, bodyToSend);
283
+
284
+ if (!isNetConnected && canSaveChangesOffline !== false) {
285
+ const body = Object.keys(bodyToSend || {}).length > 0
286
+ ? bodyToSend
287
+ : { status: bodyToSend.status }
288
+
289
+ const result = applyOffAction({
290
+ event: 'evt_off_change_order_status',
291
+ data: { orderId, body }
292
+ })
293
+
294
+ const orderUpdated = await handleUpdateOrder?.(
295
+ bodyToSend.status,
296
+ bodyToSend,
297
+ { dataToSave: { ...body, unsync: true } }
298
+ )
299
+ await registerOffOrder(orderUpdated)
300
+ closeModal && closeModal()
301
+ } else {
302
+ handleUpdateOrder?.(bodyToSend.status, bodyToSend);
303
+ }
282
304
  };
283
305
 
284
306
  useEffect(() => {
@@ -49,7 +49,7 @@ const LanguageSelectorUI = (props: LanguageSelectorParams) => {
49
49
  },
50
50
  });
51
51
 
52
- return (
52
+ return !languagesState?.loading && languagesState?.languages?.length > 1 && (
53
53
  <Container style={{ backgroundColor: theme.colors.inputChat }}>
54
54
  {languagesState?.languages && (
55
55
  <CountryPicker
@@ -1,12 +1,10 @@
1
- //React & React Native
2
1
  import React, { useState, useEffect } from 'react';
3
2
  import { StyleSheet, View } from 'react-native';
4
3
 
5
- // Thirds
6
4
  import { Placeholder, PlaceholderLine, Fade } from 'rn-placeholder';
7
5
  import Clipboard from '@react-native-clipboard/clipboard';
6
+ import MCIcon from 'react-native-vector-icons/MaterialCommunityIcons';
8
7
 
9
- //OrderingComponent
10
8
  import {
11
9
  useLanguage,
12
10
  OrderDetails as OrderDetailsConTableoller,
@@ -17,13 +15,12 @@ import {
17
15
  useConfig
18
16
  } from 'ordering-components/native';
19
17
 
20
- //Components
21
18
  import Alert from '../../providers/AlertProvider';
22
19
  import { AcceptOrRejectOrder } from '../AcceptOrRejectOrder';
23
20
  import { Chat } from '../Chat';
24
21
  import { FloatingButton } from '../FloatingButton';
25
22
  import { DriverMap } from '../DriverMap';
26
- import { OButton } from '../shared';
23
+ import { OButton, OText } from '../shared';
27
24
  import { OModal } from '../shared';
28
25
  import { OrderDetailsParams } from '../../types';
29
26
  import { USER_TYPE } from '../../config/constants';
@@ -32,7 +29,6 @@ import { NotFoundSource } from '../NotFoundSource';
32
29
  import { verifyDecimals, getProductPrice, getOrderStatus } from '../../utils';
33
30
  import { OrderHeaderComponent } from './OrderHeaderComponent';
34
31
  import { OrderContentComponent } from './OrderContentComponent';
35
- //Styles
36
32
  import { OrderDetailsContainer, Pickup } from './styles';
37
33
 
38
34
  export const OrderDetailsUI = (props: OrderDetailsParams) => {
@@ -56,8 +52,9 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
56
52
  forceUpdate,
57
53
  getPermissions,
58
54
  orderAssingId,
59
- isGrantedPermissions,
55
+ isGrantedPermissions
60
56
  } = props;
57
+
61
58
  const [, { showToast }] = useToast();
62
59
  const [{ parsePrice, parseNumber }] = useUtils();
63
60
  const [{ configs }] = useConfig();
@@ -548,6 +545,28 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
548
545
  {!((!order || Object.keys(order).length === 0) &&
549
546
  (props.order?.error?.length < 1 || !props.order?.error)) && order?.id && (
550
547
  <View style={{ flex: 1 }}>
548
+ {order?.unsync && (
549
+ <View
550
+ style={{
551
+ flexDirection: 'row',
552
+ justifyContent: 'flex-start',
553
+ alignItems: 'center'
554
+ }}
555
+ >
556
+ <MCIcon
557
+ name={'cloud-sync'}
558
+ color={'#444'}
559
+ size={16}
560
+ />
561
+ <OText
562
+ size={14}
563
+ color={theme.colors.textGray}
564
+ style={{ marginLeft: 5 }}
565
+ >
566
+ {t('PENDING_SYNC_CHANGES', 'Pending sync changes')}
567
+ </OText>
568
+ </View>
569
+ )}
551
570
  <OrderHeaderComponent
552
571
  order={order}
553
572
  handleOpenMapView={handleOpenMapView}
@@ -612,16 +631,19 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
612
631
  {showFloatButtonsAcceptOrReject[order?.status] && (
613
632
  <FloatingButton
614
633
  disabled={props.order?.loading}
634
+ widthButton={isHideRejectButtons ? '100%' : '45%'}
635
+ isHideRejectButtons={isHideRejectButtons}
615
636
  btnText={t('REJECT', 'Reject')}
637
+ firstColorCustom={theme.colors.red}
638
+ firstButtonClick={() => order?.isLogistic && (order?.order_group || logisticOrderStatus.includes(order?.status))
639
+ ? handleRejectLogisticOrder()
640
+ : handleViewActionOrder('reject')
641
+ }
616
642
  isSecondaryBtn={false}
617
- secondButtonClick={() => hideTimer ? handleChangeOrderStatus && handleChangeOrderStatus(8) : (order?.isLogistic && (order?.order_group || logisticOrderStatus.includes(order?.status))) ? handleAcceptLogisticOrder(order) : handleViewActionOrder('accept')}
618
- firstButtonClick={() => order?.isLogistic && (order?.order_group || logisticOrderStatus.includes(order?.status)) ? handleRejectLogisticOrder() : handleViewActionOrder('reject')}
619
- secondBtnText={t('ACCEPT', 'Accept')}
620
643
  secondButton={true}
621
- firstColorCustom={theme.colors.red}
644
+ secondBtnText={t('ACCEPT', 'Accept')}
645
+ secondButtonClick={() => hideTimer ? handleChangeOrderStatus && handleChangeOrderStatus(8) : (order?.isLogistic && (order?.order_group || logisticOrderStatus.includes(order?.status))) ? handleAcceptLogisticOrder(order) : handleViewActionOrder('accept')}
622
646
  secondColorCustom={theme.colors.green}
623
- widthButton={isHideRejectButtons ? '100%' : '45%'}
624
- isHideRejectButtons={isHideRejectButtons}
625
647
  />
626
648
  )}
627
649
  </>
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useState, useRef } from 'react';
2
2
  import { View, Pressable, StyleSheet, ScrollView, RefreshControl, Platform, TouchableOpacity } from 'react-native';
3
- import { useLanguage, useUtils, useToast, OrderListGroups, useConfig } from 'ordering-components/native';
3
+ import { useLanguage, useUtils, OrderListGroups, useConfig } from 'ordering-components/native';
4
4
  import SelectDropdown from 'react-native-select-dropdown'
5
5
  import { Placeholder, PlaceholderLine, Fade } from 'rn-placeholder';
6
6
  import FeatherIcon from 'react-native-vector-icons/Feather';
@@ -13,7 +13,8 @@ import { DeviceOrientationMethods } from '../../../../../src/hooks/DeviceOrienta
13
13
  import { NotificationSetting } from '../../../../../src/components/NotificationSetting'
14
14
  import { NewOrderNotification } from '../NewOrderNotification';
15
15
  import { WebsocketStatus } from '../WebsocketStatus'
16
- import { _retrieveStoreData, _setStoreData } from '../../providers/StoreUtil'
16
+ import { _retrieveStoreData, _setStoreData, _removeStoreData } from '../../providers/StoreUtil'
17
+ import { useOfflineActions } from '../../../../../src/context/OfflineActions'
17
18
 
18
19
  import { OText, OButton, OModal, OInput, OIcon } from '../shared';
19
20
  import { NotFoundSource } from '../NotFoundSource';
@@ -99,6 +100,7 @@ const OrdersOptionUI = (props: OrdersOptionParams) => {
99
100
  const [, t] = useLanguage();
100
101
  const [{ parseDate }] = useUtils()
101
102
  const [configState] = useConfig()
103
+ const [offlineActionsState] = useOfflineActions()
102
104
 
103
105
  const [orientationState] = useDeviceOrientation();
104
106
  const [openSearchModal, setOpenSearchModal] = useState(false)
@@ -406,10 +408,17 @@ const OrdersOptionUI = (props: OrdersOptionParams) => {
406
408
  const manageStoragedOrders = async () => {
407
409
  setInternetLoading(true)
408
410
  let lastConnection = await _retrieveStoreData('last_date_connection');
411
+ let allowSaveChangesOffline = await _retrieveStoreData('allow_save_changes_offline');
409
412
  let _combineTabs = await _retrieveStoreData('combine_pending_and_progress_orders')
413
+
414
+ if (allowSaveChangesOffline === false) {
415
+ setInternetLoading(false)
416
+ return
417
+ }
418
+
410
419
  let ordersStoraged: any = {}
411
420
  for (const status of orderStatuses) {
412
- ordersStoraged[status] = await _retrieveStoreData(`${status}_orders`) ?? []
421
+ ordersStoraged[status] = offlineActionsState.orders?.[status] ?? await _retrieveStoreData(`${status}_orders`) ?? []
413
422
  }
414
423
 
415
424
  if (_combineTabs || !_combineTabs && combineTabs) {
@@ -427,8 +436,9 @@ const OrdersOptionUI = (props: OrdersOptionParams) => {
427
436
 
428
437
  if (Object.values(ordersStoraged).every((key: any) => Array.isArray(key) && !key?.length)) {
429
438
  for (const status of orderStatuses) {
430
- ordersStoraged[status] = ordersGroup[status]?.orders
431
- _setStoreData(`${status}_orders`, ordersGroup[status]?.orders);
439
+ const currentOrders = offlineActionsState.orders?.[status] ?? ordersGroup[status]?.orders
440
+ ordersStoraged[status] = currentOrders
441
+ _setStoreData(`${status}_orders`, currentOrders);
432
442
  }
433
443
  }
434
444
 
@@ -449,13 +459,14 @@ const OrdersOptionUI = (props: OrdersOptionParams) => {
449
459
  };
450
460
 
451
461
  if (isNetConnected) {
452
- _setStoreData('last_date_connection', null);
453
- _setStoreData('combine_pending_and_progress_orders', null);
462
+ _removeStoreData('last_date_connection');
463
+ _removeStoreData('combine_pending_and_progress_orders');
464
+ _removeStoreData('allow_save_changes_offline');
454
465
  orderStatuses.forEach((key: any) => _setStoreData(`${key}_orders`, null))
455
466
  } else if (isNetConnected === false) {
456
467
  manageStoragedOrders()
457
468
  }
458
- }, [isNetConnected]);
469
+ }, [isNetConnected, JSON.stringify(offlineActionsState.orders)]);
459
470
 
460
471
  return (
461
472
  <>
@@ -609,7 +620,8 @@ const OrdersOptionUI = (props: OrdersOptionParams) => {
609
620
  borderRadius: 8,
610
621
  paddingVertical: 3,
611
622
  backgroundColor: theme.colors.danger500,
612
- marginBottom: 10
623
+ marginBottom: 10,
624
+ flexDirection: 'column'
613
625
  }}
614
626
  >
615
627
  <OText
@@ -617,6 +629,13 @@ const OrdersOptionUI = (props: OrdersOptionParams) => {
617
629
  >
618
630
  {`${t('LAST_UPDATE', 'Last Update')}: ${lastDateConnection}`}
619
631
  </OText>
632
+ {offlineActionsState?.actions?.length > 0 && (
633
+ <OText
634
+ style={{ color: 'white', textAlign: 'center' }}
635
+ >
636
+ {t('NUMBER_CHANGES_PENDING_SYNC', '_value_ changes pending sync').replace('_value_', offlineActionsState?.actions?.length)}
637
+ </OText>
638
+ )}
620
639
  </View>
621
640
  )}
622
641
  <ScrollView
@@ -652,7 +671,7 @@ const OrdersOptionUI = (props: OrdersOptionParams) => {
652
671
  />
653
672
  )}
654
673
  {!logisticOrders?.error?.length &&
655
- logisticOrders?.orders?.length > 0 &&
674
+ logisticOrders && logisticOrders?.orders?.length > 0 &&
656
675
  currentTabSelected === 'logisticOrders' && (
657
676
  <PreviousOrders
658
677
  orders={logisticOrders?.orders?.filter((order: any) => !order?.expired).map((order: any) => ({ ...order, isLogistic: true }))}
@@ -727,7 +746,7 @@ const OrdersOptionUI = (props: OrdersOptionParams) => {
727
746
  (currentOrdersGroup?.error?.length ||
728
747
  currentOrdersGroup?.orders?.length === 0)) ||
729
748
  (currentTabSelected === 'logisticOrders' &&
730
- (logisticOrders?.error?.length > 0 || logisticOrders?.orders?.length === 0 || !logisticOrders?.orders?.some(order => !order?.expired)))
749
+ (logisticOrders && logisticOrders?.error?.length > 0 || logisticOrders?.orders?.length === 0 || !logisticOrders?.orders?.some(order => !order?.expired)))
731
750
  ) &&
732
751
  (
733
752
  <NotFoundSource
@@ -1025,10 +1044,38 @@ export const Timer = () => {
1025
1044
  export const OrdersOption = (props: OrdersOptionParams) => {
1026
1045
  const [, t] = useLanguage();
1027
1046
  const [configState] = useConfig()
1047
+ const [, offlineMethods] = useOfflineActions()
1048
+
1028
1049
  const [checkNotificationStatus, setCheckNotificationStatus] = useState({ open: false, checked: false })
1050
+ const [combineTabs, setCombineTabs] = useState(null)
1029
1051
 
1030
- const getCombineTabsStoraged = async () => await _retrieveStoreData('combine_pending_and_progress_orders')
1031
- const combineTabs = typeof configState?.configs?.combine_pending_and_progress_orders === 'object' ? configState?.configs?.combine_pending_and_progress_orders?.value === '1' : getCombineTabsStoraged()
1052
+ useEffect(() => {
1053
+ const getCombineTabsStoraged = async () => {
1054
+ try {
1055
+ const storagedValue = await _retrieveStoreData('combine_pending_and_progress_orders');
1056
+ const saveChangesOffline = await _retrieveStoreData('allow_save_changes_offline');
1057
+
1058
+ const _combineTabs = typeof configState?.configs?.combine_pending_and_progress_orders === 'object'
1059
+ ? configState?.configs?.combine_pending_and_progress_orders?.value === '1'
1060
+ : storagedValue
1061
+
1062
+ const canSaveChangesOffline = typeof configState?.configs?.allow_save_changes_offline === 'object'
1063
+ ? (configState?.configs?.allow_save_changes_offline?.value ?? '')?.toString() === 'true'
1064
+ : saveChangesOffline
1065
+
1066
+ offlineMethods.setState((state: any) => ({
1067
+ ...state,
1068
+ isCombinedTabs: _combineTabs,
1069
+ canSaveChangesOffline
1070
+ }))
1071
+ setCombineTabs(_combineTabs)
1072
+ return _combineTabs;
1073
+ } catch {
1074
+ return null
1075
+ }
1076
+ }
1077
+ getCombineTabsStoraged()
1078
+ }, [])
1032
1079
 
1033
1080
  const ordersProps = {
1034
1081
  ...props,
@@ -2,8 +2,9 @@ import React, { useEffect, useState } from 'react';
2
2
  import { Platform, PlatformIOSStatic, Pressable, StyleSheet, View } from 'react-native';
3
3
  import DeviceInfo from 'react-native-device-info';
4
4
  import { useTheme } from 'styled-components/native';
5
- import { useLanguage, useUtils, useConfig } from 'ordering-components/native';
5
+ import { useLanguage, useUtils, useConfig, useEvent } from 'ordering-components/native';
6
6
  import EntypoIcon from 'react-native-vector-icons/Entypo'
7
+ import MCIcon from 'react-native-vector-icons/MaterialCommunityIcons';
7
8
  import FastImage from 'react-native-fast-image'
8
9
  import moment from 'moment'
9
10
 
@@ -39,10 +40,12 @@ export const OrderItem = React.memo((props: any) => {
39
40
 
40
41
  const theme = useTheme()
41
42
  const [, t] = useLanguage()
43
+ const [events] = useEvent()
42
44
  const [configState] = useConfig()
43
- const [{ parseDate, optimizeImage }] = useUtils();
45
+ const [{ parseDate }] = useUtils();
44
46
  const [orientationState] = useDeviceOrientation();
45
47
 
48
+ const [ordersOffUpdated, setOrdersOffUpdated] = useState<number[]>([])
46
49
  const [allowColumns, setAllowColumns] = useState({
47
50
  timer: configState?.configs?.order_deadlines_enabled?.value === '1',
48
51
  slaBar: configState?.configs?.order_deadlines_enabled?.value === '1',
@@ -136,6 +139,17 @@ export const OrderItem = React.memo((props: any) => {
136
139
  })
137
140
  }, [configState.loading])
138
141
 
142
+ useEffect(() => {
143
+ const handleOfflineOrder = (ids: any) => {
144
+ ids && setOrdersOffUpdated(ids)
145
+ }
146
+
147
+ events.on('offline_order_updated', handleOfflineOrder)
148
+ return () => {
149
+ events.off('offline_order_updated', handleOfflineOrder)
150
+ }
151
+ }, [])
152
+
139
153
  return (
140
154
  <Pressable
141
155
  disabled={order?.locked && isLogisticOrder}
@@ -164,11 +178,25 @@ export const OrderItem = React.memo((props: any) => {
164
178
  <OText>{(t('INVOICE_GROUP_NO', 'Group No.') + order?.order_group_id)}</OText>
165
179
  </OText>
166
180
  )}
167
- {!!order.business?.name && (
168
- <OText numberOfLines={1} style={styles.title}>
169
- {order.business?.name}
170
- </OText>
171
- )}
181
+ <View
182
+ style={{
183
+ flexDirection: 'row',
184
+ justifyContent: 'space-between'
185
+ }}
186
+ >
187
+ {!!order.business?.name && (
188
+ <OText numberOfLines={1} style={styles.title}>
189
+ {order.business?.name}
190
+ </OText>
191
+ )}
192
+ {order?.unsync && !ordersOffUpdated?.includes(order?.id) && (
193
+ <MCIcon
194
+ name={'cloud-sync'}
195
+ color={'#444'}
196
+ size={18}
197
+ />
198
+ )}
199
+ </View>
172
200
  {!!order?.showNotification && (
173
201
  <NotificationIcon>
174
202
  <EntypoIcon
@@ -1,12 +1,12 @@
1
- import React, { useState } from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import {
3
3
  Dimensions,
4
4
  Platform,
5
5
  StatusBar,
6
- StyleSheet,
7
6
  View,
8
7
  } from 'react-native';
9
- import styled from 'styled-components/native';
8
+ import styled, { useTheme } from 'styled-components/native';
9
+ import { useNetInfo } from '@react-native-community/netinfo';
10
10
 
11
11
  export const SafeAreaContainer = styled.SafeAreaView`
12
12
  flex: 1;
@@ -14,6 +14,10 @@ export const SafeAreaContainer = styled.SafeAreaView`
14
14
  `;
15
15
 
16
16
  export const SafeAreaContainerLayout = (props: any) => {
17
+ const theme = useTheme();
18
+ const netInfo = useNetInfo()
19
+
20
+ const [statusColor, setStatusColor] = useState<string | null>(null)
17
21
  const [orientation, setOrientation] = useState(
18
22
  Dimensions.get('window').width < Dimensions.get('window').height
19
23
  ? 'Portrait'
@@ -28,22 +32,34 @@ export const SafeAreaContainerLayout = (props: any) => {
28
32
  }
29
33
  });
30
34
 
35
+ useEffect(() => {
36
+ if (netInfo.isConnected === false) {
37
+ setStatusColor(theme.colors.danger500)
38
+ }
39
+
40
+ if (netInfo.isConnected && statusColor) {
41
+ setStatusColor(theme.colors.success500)
42
+ setTimeout(() => {
43
+ setStatusColor(null)
44
+ }, 2000);
45
+ }
46
+ }, [netInfo.isConnected])
47
+
31
48
  return (
32
- <>
33
- <SafeAreaContainer>
34
- <View
35
- style={{
36
- paddingHorizontal: 30,
37
- paddingTop: 30,
38
- paddingBottom: 0,
39
- flex: 1,
40
- }}>
41
- <StatusBar
42
- barStyle={Platform.OS === 'ios' ? 'dark-content' : 'default'}
43
- />
44
- {props.children}
45
- </View>
46
- </SafeAreaContainer>
47
- </>
49
+ <SafeAreaContainer>
50
+ <View
51
+ style={{
52
+ paddingHorizontal: 30,
53
+ paddingTop: 30,
54
+ paddingBottom: 0,
55
+ flex: 1,
56
+ }}>
57
+ <StatusBar
58
+ barStyle={Platform.OS === 'ios' ? 'dark-content' : 'default'}
59
+ {...statusColor && ({ backgroundColor: statusColor })}
60
+ />
61
+ {props.children}
62
+ </View>
63
+ </SafeAreaContainer>
48
64
  );
49
65
  };
@@ -321,7 +321,7 @@ export interface OrdersOptionParams {
321
321
  handleChangeOrderStatus?: () => void;
322
322
  handleSendCustomerReview?: () => void;
323
323
  orderDetailsProps?: any;
324
- isNetConnected?: boolean;
324
+ isNetConnected?: boolean | null;
325
325
  isDriverApp?: boolean;
326
326
  combineTabs?: boolean;
327
327
  setCombineTabsState?: any;
@@ -591,7 +591,7 @@ export interface AcceptOrRejectOrderParams {
591
591
  loading?: boolean;
592
592
  action: string;
593
593
  orderId?: number;
594
- handleUpdateOrder?: (p1: any, p2: any) => {};
594
+ handleUpdateOrder?: (p1: any, p2: any, p3?: any) => {};
595
595
  notShowCustomerPhone?: boolean | undefined;
596
596
  actions?: any;
597
597
  titleAccept?: textTranslate;