ordering-ui-react-native 0.15.91 → 0.15.92

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.15.91",
3
+ "version": "0.15.92",
4
4
  "description": "Reusable components made in react native",
5
5
  "main": "src/index.tsx",
6
6
  "author": "ordering.inc",
@@ -592,6 +592,19 @@ const CheckoutUI = (props: any) => {
592
592
  {t('WARNING_INVALID_PRODUCTS', 'Some products are invalid, please check them.')}
593
593
  </OText>
594
594
  )}
595
+
596
+ {options.type === 1 &&
597
+ validationFields?.fields?.checkout?.driver_tip?.enabled &&
598
+ validationFields?.fields?.checkout?.driver_tip?.required &&
599
+ (Number(cart?.driver_tip) <= 0) && (
600
+ <OText
601
+ style={{ textAlign: 'center' }}
602
+ color={theme.colors.error}
603
+ size={14}
604
+ >
605
+ {t('WARNING_INVALID_DRIVER_TIP', 'Driver Tip is required.')}
606
+ </OText>
607
+ )}
595
608
  </ChErrors>
596
609
  </ChSection>
597
610
  )}
@@ -602,8 +615,16 @@ const CheckoutUI = (props: any) => {
602
615
  <>
603
616
  <FloatingButton
604
617
  handleClick={() => handlePlaceOrder()}
605
- isSecondaryBtn={loading || !cart?.valid || !paymethodSelected || placing || errorCash || cart?.subtotal_to_calculate < cart?.minimum || paymethodSelected?.gateway === 'paypal'}
606
- disabled={loading || !cart?.valid || !paymethodSelected || placing || errorCash || cart?.subtotal_to_calculate < cart?.minimum || paymethodSelected?.gateway === 'paypal'}
618
+ isSecondaryBtn={loading || !cart?.valid || !paymethodSelected || placing || errorCash || cart?.subtotal_to_calculate < cart?.minimum || paymethodSelected?.gateway === 'paypal' ||
619
+ (options.type === 1 &&
620
+ validationFields?.fields?.checkout?.driver_tip?.enabled &&
621
+ validationFields?.fields?.checkout?.driver_tip?.required &&
622
+ (Number(cart?.driver_tip) <= 0))}
623
+ disabled={loading || !cart?.valid || !paymethodSelected || placing || errorCash || cart?.subtotal_to_calculate < cart?.minimum || paymethodSelected?.gateway === 'paypal' ||
624
+ (options.type === 1 &&
625
+ validationFields?.fields?.checkout?.driver_tip?.enabled &&
626
+ validationFields?.fields?.checkout?.driver_tip?.required &&
627
+ (Number(cart?.driver_tip) <= 0))}
607
628
  btnText={cart?.subtotal_to_calculate >= cart?.minimum
608
629
  ? (
609
630
  placing
@@ -67,7 +67,7 @@ const LoginFormUI = (props: LoginParams) => {
67
67
  const inputRef = useRef<any>(null);
68
68
  const inputMailRef = useRef<any>(null);
69
69
 
70
- const [projectName, setProjectName] = useState('');
70
+ const [projectName, setProjectName] = useState({name: '', isFocued: false});
71
71
  const [passwordSee, setPasswordSee] = useState(false);
72
72
  const [isLoadingVerifyModal, setIsLoadingVerifyModal] = useState(false);
73
73
  const [isModalVisible, setIsModalVisible] = useState(false);
@@ -125,15 +125,16 @@ const LoginFormUI = (props: LoginParams) => {
125
125
 
126
126
  useEffect(() => {
127
127
  const projectInputInterval = setInterval(() => {
128
- if (projectName && useRootPoint) {
128
+ if (projectName.name && useRootPoint && projectName.isFocued) {
129
129
  setOrdering({
130
130
  ...ordering,
131
- project: projectName
131
+ project: projectName.name
132
132
  })
133
133
  }
134
134
  }, 1500)
135
135
  return () => clearInterval(projectInputInterval);
136
136
  }, [projectName])
137
+
137
138
  const getTraduction = (key: string) => {
138
139
  const keyList: any = {
139
140
  // Add the key and traduction that you need below
@@ -515,7 +516,7 @@ const LoginFormUI = (props: LoginParams) => {
515
516
  icon={theme.images.general.project}
516
517
  iconColor={theme.colors.arrowColor}
517
518
  onChange={(e: any) => {
518
- setProjectName(e?.target?.value)
519
+ setProjectName({name: e?.target?.value, isFocued: true})
519
520
  onChange(e?.target?.value);
520
521
  setSubmitted(false);
521
522
  }}
@@ -544,6 +545,7 @@ const LoginFormUI = (props: LoginParams) => {
544
545
  icon={theme.images.logos.emailInputIcon}
545
546
  iconColor={theme.colors.arrowColor}
546
547
  onChange={(e: any) => {
548
+ setProjectName({...projectName, isFocued: false})
547
549
  handleChangeInputEmail(e, onChange);
548
550
  }}
549
551
  selectionColor={theme.colors.primary}
@@ -23,7 +23,7 @@ const NewOrderNotificationUI = (props: any) => {
23
23
  const [ordering] = useApi()
24
24
  const { getCurrentLocation } = useLocation();
25
25
  const [soundTimeout, setSoundTimeout] = useState<any>(null)
26
- const [currentEvent, setCurrentEvent] = useState<any>(null)
26
+ let [currentEvent, setCurrentEvent] = useState<any>(null)
27
27
 
28
28
  const evtList: any = {
29
29
  1: {
@@ -46,6 +46,7 @@ const NewOrderNotificationUI = (props: any) => {
46
46
  const notificationSound = new Sound(theme.sounds.notification, (e) => { console.log(e) });
47
47
 
48
48
  const handlePlayNotificationSound = () => {
49
+ if (currentEvent) return
49
50
  let times = 0
50
51
  const _timeout = setInterval(function () {
51
52
  notificationSound.play(success => {
@@ -63,30 +64,40 @@ const NewOrderNotificationUI = (props: any) => {
63
64
 
64
65
  const handleCloseModal = () => {
65
66
  clearInterval(soundTimeout)
67
+ currentEvent = null
66
68
  setCurrentEvent({ evt: null })
67
69
  }
68
70
 
69
71
  const handleEventNotification = async (evtType: number, value: any) => {
70
72
  if (value?.driver) {
71
- const location = await getCurrentLocation()
72
- await fetch(`${ordering.root}/users/${user.id}/locations`, {
73
- method: 'POST',
74
- body: JSON.stringify({
75
- location: JSON.stringify({location: `{lat: ${location.latitude}, lng: ${location.longitude}}`})
76
- }),
77
- headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }
78
- })
79
- const assignedTimeDiff = moment.utc(value?.driver?.last_order_assigned_at).local().fromNow()
80
- if (assignedTimeDiff === 'a few seconds ago' && !isBusinessApp) {
73
+ try {
74
+ const location = await getCurrentLocation()
75
+ await fetch(`${ordering.root}/users/${user.id}/locations`, {
76
+ method: 'POST',
77
+ body: JSON.stringify({
78
+ location: JSON.stringify({location: `{lat: ${location.latitude}, lng: ${location.longitude}}`})
79
+ }),
80
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }
81
+ })
82
+ } catch (error) {
83
+ console.log(error)
84
+ }
85
+ const duration = moment.duration(moment().diff(moment.utc(value?.last_driver_assigned_at)))
86
+ const assignedSecondsDiff = duration.asSeconds()
87
+ if (assignedSecondsDiff < 5 && !isBusinessApp) {
81
88
  handlePlayNotificationSound()
82
89
  clearInterval(soundTimeout)
90
+ currentEvent = { evt: 2, orderId: value?.id }
83
91
  setCurrentEvent({ evt: 2, orderId: value?.id })
84
92
  }
85
- return
86
93
  }
87
- if (evtType === 3) return
94
+ if (evtType === 3 || value.author_id === user.id) return
88
95
  handlePlayNotificationSound()
89
96
  clearInterval(soundTimeout)
97
+ currentEvent = {
98
+ evt: evtType,
99
+ orderId: evtList[evtType].event === 'messages' ? value?.order_id : value?.id
100
+ }
90
101
  setCurrentEvent({
91
102
  evt: evtType,
92
103
  orderId: evtList[evtType].event === 'messages' ? value?.order_id : value?.id
@@ -77,6 +77,7 @@ export const OrderContentComponent = (props: OrderContent) => {
77
77
  })
78
78
 
79
79
  const getIncludedTaxes = () => {
80
+ if (!order?.taxes) return 0
80
81
  if (order?.taxes?.length === 0) {
81
82
  return order.tax_type === 1 ? order?.summary?.tax ?? 0 : 0
82
83
  } else {
@@ -725,8 +725,11 @@ const OrdersOptionUI = (props: OrdersOptionParams) => {
725
725
  </ScrollView>
726
726
  </View>
727
727
  {/* </GestureRecognizer> */}
728
-
729
- <NewOrderNotification isBusinessApp={isBusinessApp} />
728
+
729
+ {isBusinessApp && (
730
+ <NewOrderNotification isBusinessApp={isBusinessApp} />
731
+ )}
732
+
730
733
  {(openSearchModal || openSLASettingModal) && (
731
734
  <OModal open={openSearchModal || openSLASettingModal} entireModal customClose>
732
735
  <ModalContainer
@@ -24,7 +24,8 @@ import {
24
24
  TagsContainer,
25
25
  SortContainer,
26
26
  BrandContainer,
27
- BrandItem
27
+ BrandItem,
28
+ PriceFilterWrapper
28
29
  } from './styles'
29
30
  import FastImage from 'react-native-fast-image'
30
31
  import { convertHoursToMinutes } from '../../utils'
@@ -67,6 +68,14 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
67
68
  { text: t('PICKUP_TIME', 'Pickup time'), value: 'pickup_time' }
68
69
  ]
69
70
 
71
+ const priceList = [
72
+ { level: '1', content: '$' },
73
+ { level: '2', content: '$$' },
74
+ { level: '3', content: '$$$' },
75
+ { level: '4', content: '$$$$' },
76
+ { level: '5', content: '$$$$$' }
77
+ ]
78
+
70
79
  const styles = StyleSheet.create({
71
80
  container: {
72
81
  paddingHorizontal: 40,
@@ -93,6 +102,12 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
93
102
  flexWrap: 'wrap',
94
103
  justifyContent: 'center'
95
104
  },
105
+ priceContainer: {
106
+ width: '100%',
107
+ flexDirection: 'row',
108
+ flexWrap: 'wrap',
109
+ justifyContent: 'space-between'
110
+ },
96
111
  categoryStyle: {
97
112
  marginRight: 10,
98
113
  marginTop: 10,
@@ -104,6 +119,16 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
104
119
  height: 28,
105
120
  borderWidth: 0
106
121
  },
122
+ priceItem: {
123
+ marginRight: 10,
124
+ marginTop: 10,
125
+ borderRadius: 50,
126
+ paddingVertical: 4,
127
+ paddingLeft: 5,
128
+ paddingRight: 5,
129
+ height: 27,
130
+ borderWidth: 0
131
+ },
107
132
  applyButton: {
108
133
  paddingHorizontal: 40,
109
134
  width: '100%',
@@ -116,7 +141,7 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
116
141
  }
117
142
 
118
143
  const handleCloseFilters = () => {
119
- setFilters({ business_types: [], orderBy: 'default', franchise_ids: [] })
144
+ setFilters({ business_types: [], orderBy: 'default', franchise_ids: [], price_level: null })
120
145
  setOpenFilters(false)
121
146
  }
122
147
 
@@ -142,6 +167,11 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
142
167
  handleChangeFilters && handleChangeFilters('franchise_ids', franchiseIds)
143
168
  }
144
169
 
170
+ const handleChangePriceRange = (value: string) => {
171
+ if (value === filters?.price_level) handleChangeFilters('price_level', null)
172
+ else handleChangeFilters('price_level', value)
173
+ }
174
+
145
175
  const handleApplyFilters = () => {
146
176
  handleSearchbusinessAndProducts(true)
147
177
  setOpenFilters(false)
@@ -473,6 +503,28 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
473
503
  <OText size={14} weight='400'>{t('NO_RESULTS_FOUND', 'Sorry, no results found')}</OText>
474
504
  )}
475
505
  </BrandContainer>
506
+ <PriceFilterWrapper>
507
+ <OText
508
+ size={16}
509
+ weight='bold'
510
+ lineHeight={24}
511
+ style={{ marginBottom: 5 }}
512
+ >
513
+ {t('PRICE_RANGE', 'Price range')}
514
+ </OText>
515
+ <View style={styles.priceContainer}>
516
+ {priceList.map((price: any, i: number) => (
517
+ <OButton
518
+ key={i}
519
+ bgColor={(filters?.price_level === price?.level) ? theme.colors.primary : theme.colors.backgroundGray200}
520
+ onClick={() => handleChangePriceRange(price?.level)}
521
+ text={`${price.content} ${(filters?.price_level === price?.level) ? ' X' : ''}`}
522
+ style={styles.priceItem}
523
+ textStyle={{ fontSize: 10, color: (filters?.price_level === price?.level) ? theme.colors.backgroundLight : theme.colors.textNormal }}
524
+ />
525
+ ))}
526
+ </View>
527
+ </PriceFilterWrapper>
476
528
  {orderState?.options?.type === 1 && (
477
529
  <MaxSectionItem
478
530
  title={t('MAX_DELIVERY_FEE', 'Max delivery fee')}
@@ -83,3 +83,7 @@ export const BrandItem = styled.TouchableOpacity`
83
83
  margin-bottom: 4px;
84
84
  align-items: center;
85
85
  `
86
+
87
+ export const PriceFilterWrapper = styled.View`
88
+ margin-bottom: 20px;
89
+ `
@@ -59,7 +59,8 @@ const BusinessesListingUI = (props: BusinessesListingParams) => {
59
59
  handleBusinessClick,
60
60
  paginationProps,
61
61
  handleChangeSearch,
62
- businessId
62
+ businessId,
63
+ isGuestUser
63
64
  } = props;
64
65
  const theme = useTheme();
65
66
  const isFocused = useIsFocused();
@@ -242,6 +243,7 @@ const BusinessesListingUI = (props: BusinessesListingParams) => {
242
243
  : navigation.navigate('AddressForm', {
243
244
  address: orderState.options?.address,
244
245
  isFromBusinesses: true,
246
+ isGuestUser: isGuestUser
245
247
  })
246
248
  }>
247
249
  <OIcon
@@ -113,7 +113,10 @@ const CheckoutUI = (props: any) => {
113
113
  right: Platform.OS === 'ios' ? 5 : (I18nManager.isRTL ? 30 : 0),
114
114
  position: 'absolute',
115
115
  fontSize: 20
116
- }
116
+ },
117
+ wrapperNavbar: Platform.OS === 'ios'
118
+ ? { paddingVertical: 0, paddingHorizontal: 40 }
119
+ : { paddingVertical: 20, paddingHorizontal: 40 }
117
120
  })
118
121
 
119
122
  const [, { showToast }] = useToast();
@@ -133,20 +136,20 @@ const CheckoutUI = (props: any) => {
133
136
  const [isDeliveryOptionModalVisible, setIsDeliveryOptionModalVisible] = useState(false)
134
137
  const [showGateway, setShowGateway] = useState<any>({ closedByUsed: false, open: false });
135
138
  const [webviewPaymethod, setWebviewPaymethod] = useState<any>(null)
136
-
139
+
137
140
  const placeSpotTypes = [3, 4]
138
- const businessConfigs = businessDetails?.business?.configs ?? []
139
- const isWalletCashEnabled = businessConfigs.find((config: any) => config.key === 'wallet_cash_enabled')?.value === '1'
140
- const isWalletCreditPointsEnabled = businessConfigs.find((config: any) => config.key === 'wallet_credit_point_enabled')?.value === '1'
141
- const isWalletEnabled = configs?.cash_wallet?.value && configs?.wallet_enabled?.value === '1' && (isWalletCashEnabled || isWalletCreditPointsEnabled)
141
+ const businessConfigs = businessDetails?.business?.configs ?? []
142
+ const isWalletCashEnabled = businessConfigs.find((config: any) => config.key === 'wallet_cash_enabled')?.value === '1'
143
+ const isWalletCreditPointsEnabled = businessConfigs.find((config: any) => config.key === 'wallet_credit_point_enabled')?.value === '1'
144
+ const isWalletEnabled = configs?.cash_wallet?.value && configs?.wallet_enabled?.value === '1' && (isWalletCashEnabled || isWalletCreditPointsEnabled)
142
145
 
143
146
  const isPreOrder = configs?.preorder_status_enabled?.value === '1'
144
147
  const isDisabledButtonPlace = loading || !cart?.valid || (!paymethodSelected && cart?.balance > 0) || placing || errorCash ||
145
- cart?.subtotal < cart?.minimum || (placeSpotTypes.includes(options?.type) && !cart?.place) ||
146
- (options.type === 1 &&
147
- validationFields?.fields?.checkout?.driver_tip?.enabled &&
148
- validationFields?.fields?.checkout?.driver_tip?.required &&
149
- (Number(cart?.driver_tip) <= 0))
148
+ cart?.subtotal < cart?.minimum || (placeSpotTypes.includes(options?.type) && !cart?.place) ||
149
+ (options.type === 1 &&
150
+ validationFields?.fields?.checkout?.driver_tip?.enabled &&
151
+ validationFields?.fields?.checkout?.driver_tip?.required &&
152
+ (Number(cart?.driver_tip) <= 0))
150
153
 
151
154
  const driverTipsOptions = typeof configs?.driver_tip_options?.value === 'string'
152
155
  ? JSON.parse(configs?.driver_tip_options?.value) || []
@@ -259,13 +262,18 @@ const CheckoutUI = (props: any) => {
259
262
  return (
260
263
  <>
261
264
  <Container noPadding>
262
- <NavBar
263
- isVertical
264
- onActionLeft={() => navigation?.canGoBack() && navigation.goBack()}
265
- title={t('CHECKOUT', 'Checkout')}
266
- titleStyle={{ marginLeft: 0, marginRight: 0, paddingLeft: 40 }}
267
- btnStyle={{ marginLeft: 40, padding: 40 }}
268
- />
265
+ <View style={styles.wrapperNavbar}>
266
+ <NavBar
267
+ title={t('CHECKOUT', 'Checkout')}
268
+ titleAlign={'center'}
269
+ onActionLeft={() => navigation?.canGoBack() && navigation.goBack()}
270
+ showCall={false}
271
+ btnStyle={{ paddingLeft: 0 }}
272
+ style={{ flexDirection: 'column', alignItems: 'flex-start', marginTop: Platform.OS === 'ios' ? 0 : 20 }}
273
+ titleWrapStyle={{ paddingHorizontal: 0 }}
274
+ titleStyle={{ marginRight: 0, marginLeft: 0 }}
275
+ />
276
+ </View>
269
277
  <ChContainer style={styles.pagePadding}>
270
278
  <ChSection style={{ paddingTop: 0 }}>
271
279
  <ChHeader>
@@ -668,16 +676,16 @@ const CheckoutUI = (props: any) => {
668
676
  </OText>
669
677
  )}
670
678
  {options.type === 1 &&
671
- validationFields?.fields?.checkout?.driver_tip?.enabled &&
672
- validationFields?.fields?.checkout?.driver_tip?.required &&
673
- (Number(cart?.driver_tip) <= 0) && (
674
- <OText
675
- color={theme.colors.error}
676
- size={12}
677
- >
678
- {t('WARNING_INVALID_DRIVER_TIP', 'Driver Tip is required.')}
679
- </OText>
680
- )}
679
+ validationFields?.fields?.checkout?.driver_tip?.enabled &&
680
+ validationFields?.fields?.checkout?.driver_tip?.required &&
681
+ (Number(cart?.driver_tip) <= 0) && (
682
+ <OText
683
+ color={theme.colors.error}
684
+ size={12}
685
+ >
686
+ {t('WARNING_INVALID_DRIVER_TIP', 'Driver Tip is required.')}
687
+ </OText>
688
+ )}
681
689
  </ChErrors>
682
690
  </View>
683
691
  )}
@@ -1,9 +1,11 @@
1
- import React from 'react'
1
+ import React, { useState } from 'react'
2
+ import { RefreshControl } from 'react-native'
2
3
  import { HelpParams } from '../../types'
3
4
  import { useLanguage } from 'ordering-components/native'
4
5
  import NavBar from '../NavBar'
5
6
  import { OText } from '../shared'
6
7
  import { LastOrders } from '../LastOrders'
8
+ import { Container } from '../../layouts/Container'
7
9
 
8
10
  import {
9
11
  HelpSubItem,
@@ -15,13 +17,28 @@ export const Help = (props: HelpParams) => {
15
17
  navigation
16
18
  } = props
17
19
  const [, t] = useLanguage()
20
+ const [refreshing] = useState(false);
21
+ const [refresh, setRefresh] = useState(false)
18
22
 
19
23
  const goToBack = () => navigation?.canGoBack() && navigation.goBack()
20
24
  const onRedirect = (route: string, params?: any) => {
21
25
  navigation.navigate(route, params)
22
26
  }
27
+
28
+ const handleOnRefresh = () => {
29
+ setRefresh(true)
30
+ }
31
+
23
32
  return (
24
- <>
33
+ <Container
34
+ noPadding
35
+ refreshControl={
36
+ <RefreshControl
37
+ refreshing={refreshing}
38
+ onRefresh={() => handleOnRefresh()}
39
+ />
40
+ }
41
+ >
25
42
  <NavBar
26
43
  title={t('HELP', 'Help')}
27
44
  titleAlign={'center'}
@@ -48,8 +65,8 @@ export const Help = (props: HelpParams) => {
48
65
 
49
66
  <LastOrdersContainer>
50
67
  <OText size={18} weight={600}>{t('LAST_ORDERS', 'Last Orders')}</OText>
51
- <LastOrders {...props} onRedirect={onRedirect} />
68
+ <LastOrders {...props} onRedirect={onRedirect} refresh={refresh} setRefresh={setRefresh} />
52
69
  </LastOrdersContainer>
53
- </>
70
+ </Container>
54
71
  )
55
72
  }
@@ -18,11 +18,15 @@ import {
18
18
  OrderContainer,
19
19
  OrderInfo
20
20
  } from './styles'
21
+ import { useEffect } from 'react'
21
22
 
22
23
  const LastOrdersUI = (props: LastOrdersParams) => {
23
24
  const {
24
25
  orderList,
25
- onRedirect
26
+ onRedirect,
27
+ loadOrders,
28
+ refresh,
29
+ setRefresh
26
30
  } = props
27
31
  const { loading, error, orders } = orderList
28
32
 
@@ -46,6 +50,13 @@ const LastOrdersUI = (props: LastOrdersParams) => {
46
50
  onRedirect && onRedirect('OrderDetails', { orderId: uuid })
47
51
  }
48
52
 
53
+ useEffect(() => {
54
+ if(refresh){
55
+ loadOrders(false, false, false, true)
56
+ setRefresh && setRefresh(false)
57
+ }
58
+ }, [refresh])
59
+
49
60
  return (
50
61
  <>
51
62
  {loading ? (
@@ -93,6 +93,8 @@ const LoginFormUI = (props: LoginParams) => {
93
93
  const [recaptchaConfig, setRecaptchaConfig] = useState<any>({})
94
94
  const [recaptchaVerified, setRecaptchaVerified] = useState(false)
95
95
  const [alertState, setAlertState] = useState({ open: false, title: '', content: [] })
96
+ const [tabLayouts, setTabLayouts] = useState<any>({})
97
+ const tabsRef = useRef<any>(null)
96
98
 
97
99
  const theme = useTheme();
98
100
  const isOtpEmail = loginTab === 'otp' && otpType === 'email'
@@ -146,9 +148,10 @@ const LoginFormUI = (props: LoginParams) => {
146
148
  const passwordRef = useRef<any>({});
147
149
  const recaptchaRef = useRef<any>({});
148
150
 
149
- const handleChangeTab = (val: string) => {
151
+ const handleChangeTab = (val: string, otpType?: string) => {
150
152
  props.handleChangeTab(val);
151
153
  setPasswordSee(false);
154
+ handleCategoryScroll(otpType ? `${val}_${otpType}` : val)
152
155
  };
153
156
 
154
157
  const onSubmit = (values?: any) => {
@@ -231,7 +234,7 @@ const LoginFormUI = (props: LoginParams) => {
231
234
  }
232
235
 
233
236
  const handleChangeOtpType = (type: string) => {
234
- handleChangeTab('otp')
237
+ handleChangeTab('otp', type)
235
238
  setOtpType(type)
236
239
  }
237
240
 
@@ -248,6 +251,20 @@ const LoginFormUI = (props: LoginParams) => {
248
251
  })
249
252
  }
250
253
 
254
+ const handleCategoryScroll = (opc : string) => {
255
+ tabsRef.current.scrollTo({
256
+ x: tabLayouts?.[opc]?.x - 40,
257
+ animated: true
258
+ })
259
+ }
260
+
261
+ const handleOnLayout = (event: any, opc: string) => {
262
+ const _tabLayouts = { ...tabLayouts }
263
+ const categoryKey = opc
264
+ _tabLayouts[categoryKey] = event.nativeEvent.layout
265
+ setTabLayouts(_tabLayouts)
266
+ }
267
+
251
268
  useEffect(() => {
252
269
  if (configs && Object.keys(configs).length > 0 && enableReCaptcha) {
253
270
  setRecaptchaConfig({
@@ -331,9 +348,16 @@ const LoginFormUI = (props: LoginParams) => {
331
348
  <FormSide>
332
349
  {((useLoginByEmail && useLoginByCellphone) || useLoginOtp) && (
333
350
  <LoginWith>
334
- <OTabs horizontal>
351
+ <OTabs
352
+ horizontal
353
+ showsHorizontalScrollIndicator={false}
354
+ ref={tabsRef}
355
+ >
335
356
  {useLoginByEmail && (
336
- <TabBtn onPress={() => handleChangeTab('email')}>
357
+ <TabBtn
358
+ onPress={() => handleChangeTab('email')}
359
+ onLayout={(event: any) => handleOnLayout(event, 'email')}
360
+ >
337
361
  <OTab
338
362
  style={{
339
363
  borderBottomColor:
@@ -355,7 +379,10 @@ const LoginFormUI = (props: LoginParams) => {
355
379
  </TabBtn>
356
380
  )}
357
381
  {useLoginByCellphone && (
358
- <TabBtn onPress={() => handleChangeTab('cellphone')}>
382
+ <TabBtn
383
+ onPress={() => handleChangeTab('cellphone')}
384
+ onLayout={(event: any) => handleOnLayout(event, 'cellphone')}
385
+ >
359
386
  <OTab
360
387
  style={{
361
388
  borderBottomColor:
@@ -377,7 +404,10 @@ const LoginFormUI = (props: LoginParams) => {
377
404
  </TabBtn>
378
405
  )}
379
406
  {useLoginOtpEmail && (
380
- <TabBtn onPress={() => handleChangeOtpType('email')}>
407
+ <TabBtn
408
+ onPress={() => handleChangeOtpType('email')}
409
+ onLayout={(event: any) => handleOnLayout(event, 'otp_email')}
410
+ >
381
411
  <OTab
382
412
  style={{
383
413
  borderBottomColor:
@@ -399,7 +429,10 @@ const LoginFormUI = (props: LoginParams) => {
399
429
  </TabBtn>
400
430
  )}
401
431
  {useLoginOtpCellphone && (
402
- <TabBtn onPress={() => handleChangeOtpType('cellphone')}>
432
+ <TabBtn
433
+ onPress={() => handleChangeOtpType('cellphone')}
434
+ onLayout={(event: any) => handleOnLayout(event, 'otp_cellphone')}
435
+ >
403
436
  <OTab
404
437
  style={{
405
438
  borderBottomColor:
@@ -42,7 +42,7 @@ import { OSRow } from '../OrderSummary/styles';
42
42
  import AntIcon from 'react-native-vector-icons/AntDesign'
43
43
  import { TaxInformation } from '../TaxInformation';
44
44
  import { Placeholder, PlaceholderLine } from 'rn-placeholder';
45
-
45
+ import NavBar from '../NavBar'
46
46
  export const OrderDetailsUI = (props: OrderDetailsParams) => {
47
47
  const {
48
48
  navigation,
@@ -82,7 +82,8 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
82
82
  justifyContent: 'flex-start',
83
83
  paddingLeft: 0,
84
84
  height: 30,
85
- width: 40,
85
+ width: 30,
86
+ marginTop: Platform.OS === 'ios' ? 0 : 30
86
87
  },
87
88
  });
88
89
 
@@ -364,7 +365,7 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
364
365
  }
365
366
 
366
367
  const RenderGoogleMap = () => {
367
- const driverLocationString = typeof order?.driver?.location?.location === 'string' && order?.driver?.location?.location?.split(',').map((l : string) => l.replace(/[^-.0-9]/g, ''))
368
+ const driverLocationString = typeof order?.driver?.location?.location === 'string' && order?.driver?.location?.location?.split(',').map((l: string) => l.replace(/[^-.0-9]/g, ''))
368
369
  const parsedLocations = locations.map(location => typeof location?.location === 'string' ? {
369
370
  ...location,
370
371
  lat: parseFloat(location?.location?.split(',')[0].replace(/[^-.0-9]/g, '')),
@@ -498,27 +499,23 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
498
499
  {order && Object.keys(order).length > 0 && (
499
500
  <>
500
501
  <Header>
501
- <OButton
502
- imgLeftSrc={theme.images.general.arrow_left}
503
- imgRightSrc={null}
504
- style={styles.btnBackArrow}
505
- onClick={() => handleArrowBack()}
506
- imgLeftStyle={{ tintColor: theme.colors.disabled }}
502
+ <NavBar
503
+ title={`${t('ORDER', 'Order')} #${order?.id}`}
504
+ titleAlign={'center'}
505
+ onActionLeft={handleArrowBack}
506
+ showCall={false}
507
+ btnStyle={{ paddingLeft: 0 }}
508
+ style={{ flexDirection: 'column', alignItems: 'flex-start', marginTop: Platform.OS === 'ios' ? 0 : 20 }}
509
+ titleWrapStyle={{ paddingHorizontal: 0 }}
510
+ titleStyle={{ marginRight: 0, marginLeft: 0 }}
511
+ subTitle={<OText size={12} lineHeight={18} color={theme.colors.textNormal}>
512
+ {order?.delivery_datetime_utc
513
+ ? parseDate(order?.delivery_datetime_utc)
514
+ : parseDate(order?.delivery_datetime, { utc: false })}
515
+ </OText>}
507
516
  />
508
517
  <OrderInfo>
509
518
  <OrderData>
510
- <OText
511
- size={20}
512
- lineHeight={30}
513
- weight={'600'}
514
- color={theme.colors.textNormal}>
515
- {t('ORDER', 'Order')} #{order?.id}
516
- </OText>
517
- <OText size={12} lineHeight={18} color={theme.colors.textNormal}>
518
- {order?.delivery_datetime_utc
519
- ? parseDate(order?.delivery_datetime_utc)
520
- : parseDate(order?.delivery_datetime, { utc: false })}
521
- </OText>
522
519
  {
523
520
  (
524
521
  parseInt(order?.status) === 1 ||
@@ -112,6 +112,7 @@ const PaymentOptionWalletUI = (props: any) => {
112
112
  true: theme.colors.primary,
113
113
  false: theme.colors.disabled
114
114
  }}
115
+ onChange={() => handleOnChange(idx, wallet)}
115
116
  tintColor={theme.colors.disabled}
116
117
  onCheckColor={theme.colors.primary}
117
118
  onTintColor={theme.colors.primary}
@@ -17,7 +17,7 @@ import { useTheme } from 'styled-components/native';
17
17
  import { OButton, OIcon, OModal, OText } from '../shared'
18
18
  import { Placeholder, PlaceholderLine } from 'rn-placeholder'
19
19
  import { NotFoundSource } from '../NotFoundSource'
20
- import { View, StyleSheet, ScrollView, Platform } from 'react-native'
20
+ import { View, StyleSheet, ScrollView, Platform, RefreshControl } from 'react-native'
21
21
  import FastImage from 'react-native-fast-image'
22
22
  import { PromotionParams } from '../../types'
23
23
  import { Container } from '../../layouts/Container'
@@ -28,6 +28,7 @@ const PromotionsUI = (props: PromotionParams) => {
28
28
  offersState,
29
29
  handleSearchValue,
30
30
  searchValue,
31
+ loadOffers,
31
32
  offerSelected,
32
33
  setOfferSelected
33
34
  } = props
@@ -68,6 +69,7 @@ const PromotionsUI = (props: PromotionParams) => {
68
69
  const [, t] = useLanguage()
69
70
  const [{ parseDate, parsePrice, optimizeImage }] = useUtils()
70
71
  const [openModal, setOpenModal] = useState(false)
72
+ const [refreshing] = useState(false);
71
73
 
72
74
  const handleClickOffer = (offer: any) => {
73
75
  setOpenModal(true)
@@ -79,6 +81,12 @@ const PromotionsUI = (props: PromotionParams) => {
79
81
  navigation.navigate('Business', { store: store.slug })
80
82
  }
81
83
 
84
+ const handleOnRefresh = () => {
85
+ if (!offersState.loading) {
86
+ loadOffers();
87
+ }
88
+ }
89
+
82
90
  const filteredOffers = offersState?.offers?.filter((offer: any) => offer.name.toLowerCase().includes(searchValue.toLowerCase()))
83
91
  const targetString = offerSelected?.target === 1
84
92
  ? t('SUBTOTAL', 'Subtotal')
@@ -87,7 +95,15 @@ const PromotionsUI = (props: PromotionParams) => {
87
95
  : t('SERVICE_FEE', 'Service fee')
88
96
 
89
97
  return (
90
- <Container noPadding>
98
+ <Container
99
+ noPadding
100
+ refreshControl={
101
+ <RefreshControl
102
+ refreshing={refreshing}
103
+ onRefresh={() => handleOnRefresh()}
104
+ />
105
+ }
106
+ >
91
107
  <NavBar
92
108
  title={t('PROMOTIONS', 'Promotions')}
93
109
  titleAlign={'center'}
@@ -153,6 +153,7 @@ export interface BusinessesListingParams {
153
153
  defaultBusinessType?: any;
154
154
  franchiseId?: any;
155
155
  businessId?: any;
156
+ isGuestUser?: any;
156
157
  }
157
158
  export interface HighestRatedBusinessesParams {
158
159
  businessesList: { businesses: Array<any>, loading: boolean, error: null | string };