ordering-ui-react-native 0.12.65 → 0.12.69

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.12.65",
3
+ "version": "0.12.69",
4
4
  "description": "Reusable components made in react native",
5
5
  "main": "src/index.tsx",
6
6
  "author": "ordering.inc",
@@ -25,6 +25,8 @@ import {
25
25
  import { useTheme } from 'styled-components/native';
26
26
  import { ReviewCustomer } from '../ReviewCustomer'
27
27
 
28
+ import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons'
29
+
28
30
  interface OrderContent {
29
31
  order: any,
30
32
  logisticOrderStatus?: Array<number>,
@@ -150,46 +152,56 @@ export const OrderContentComponent = (props: OrderContent) => {
150
152
  {
151
153
  (order?.customer?.name || order?.customer?.lastname) && (
152
154
  <View style={{ flexDirection: 'row' }}>
153
- <OText numberOfLines={2} mBottom={4}>
154
- {order?.customer?.name && (
155
- <OText
156
- numberOfLines={1}
157
- mBottom={4}
158
- ellipsizeMode="tail"
159
- space>
160
- {order?.customer?.name}
161
- </OText>
162
- )}
163
- {order?.customer?.middle_name && (
164
- <OText
165
- numberOfLines={1}
166
- mBottom={4}
167
- ellipsizeMode="tail"
168
- space>
169
- {order?.customer?.middle_name}
170
- </OText>
171
- )}
172
- {order?.customer?.lastname && (
173
- <OText
174
- numberOfLines={1}
175
- mBottom={4}
176
- ellipsizeMode="tail"
177
- space>
178
- {order?.customer?.lastname}
179
- </OText>
180
- )}
181
- {
182
- order?.customer?.second_lastname && (
155
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between', flex: 1 }}>
156
+ <View style={{ flexDirection: 'row' }}>
157
+ {order?.customer?.name && (
183
158
  <OText
184
159
  numberOfLines={1}
185
160
  mBottom={4}
186
161
  ellipsizeMode="tail"
187
162
  space>
188
- {order?.customer?.second_lastname}
163
+ {order?.customer?.name}
189
164
  </OText>
190
- )
191
- }
192
- </OText>
165
+ )}
166
+ {order?.customer?.middle_name && (
167
+ <OText
168
+ numberOfLines={1}
169
+ mBottom={4}
170
+ ellipsizeMode="tail"
171
+ space>
172
+ {order?.customer?.middle_name}
173
+ </OText>
174
+ )}
175
+ {order?.customer?.lastname && (
176
+ <OText
177
+ numberOfLines={1}
178
+ mBottom={4}
179
+ ellipsizeMode="tail"
180
+ space>
181
+ {order?.customer?.lastname}
182
+ </OText>
183
+ )}
184
+ {
185
+ order?.customer?.second_lastname && (
186
+ <OText
187
+ numberOfLines={1}
188
+ mBottom={4}
189
+ ellipsizeMode="tail"
190
+ space>
191
+ {order?.customer?.second_lastname}
192
+ </OText>
193
+ )
194
+ }
195
+ </View>
196
+ {order?.user_review?.qualification && (
197
+ <View style={{ flexDirection: 'row' }}>
198
+ <MaterialIcon style={{ bottom: 2 }} name='star' size={24} color={theme.colors.arrowColor} />
199
+ <OText mLeft={5}>
200
+ {order?.user_review?.qualification}
201
+ </OText>
202
+ </View>
203
+ )}
204
+ </View>
193
205
  </View>
194
206
  )
195
207
  }
@@ -1,13 +1,13 @@
1
1
  import React, { useState, useRef, useCallback } from 'react'
2
2
  import { View, TouchableOpacity, StyleSheet, TextStyle, ScrollView, I18nManager, Platform } from 'react-native'
3
3
  import {
4
- BusinessAndProductList,
5
- useLanguage,
6
- useOrder,
7
- useSession,
8
- useUtils,
9
- useToast,
10
- ToastType
4
+ BusinessAndProductList,
5
+ useLanguage,
6
+ useOrder,
7
+ useSession,
8
+ useUtils,
9
+ useToast,
10
+ ToastType
11
11
  } from 'ordering-components/native'
12
12
  import { OButton, OIcon, OModal, OText } from '../shared'
13
13
  import { BusinessBasicInformation } from '../BusinessBasicInformation'
@@ -16,15 +16,15 @@ import { BusinessProductsCategories } from '../BusinessProductsCategories'
16
16
  import { BusinessProductsList } from '../BusinessProductsList'
17
17
  import { BusinessProductsListingParams } from '../../types'
18
18
  import {
19
- WrapHeader,
20
- TopHeader,
21
- AddressInput,
22
- WrapSearchBar,
23
- WrapContent,
24
- BusinessProductsListingContainer,
25
- SortWrap,
26
- SortButton,
27
- CategoryWrap,
19
+ WrapHeader,
20
+ TopHeader,
21
+ AddressInput,
22
+ WrapSearchBar,
23
+ WrapContent,
24
+ BusinessProductsListingContainer,
25
+ SortWrap,
26
+ SortButton,
27
+ CategoryWrap,
28
28
  } from './styles'
29
29
  import { useTheme } from 'styled-components/native'
30
30
  import { FloatingButton } from '../FloatingButton'
@@ -40,347 +40,347 @@ import SocialShareFav from '../SocialShare'
40
40
  const PIXELS_TO_SCROLL = 1000
41
41
 
42
42
  const BusinessProductsListingUI = (props: BusinessProductsListingParams) => {
43
- const {
44
- navigation,
45
- errors,
46
- businessState,
47
- categoryState,
48
- handleChangeSearch,
49
- categorySelected,
50
- searchValue,
51
- handleSearchRedirect,
52
- featuredProducts,
53
- errorQuantityProducts,
54
- header,
55
- logo,
56
- productModal,
57
- handleChangeCategory,
58
- setProductLogin,
59
- updateProductModal,
60
- getNextProducts
61
- } = props
62
- const theme = useTheme()
43
+ const {
44
+ navigation,
45
+ errors,
46
+ businessState,
47
+ categoryState,
48
+ handleChangeSearch,
49
+ categorySelected,
50
+ searchValue,
51
+ handleSearchRedirect,
52
+ featuredProducts,
53
+ errorQuantityProducts,
54
+ header,
55
+ logo,
56
+ productModal,
57
+ handleChangeCategory,
58
+ setProductLogin,
59
+ updateProductModal,
60
+ getNextProducts
61
+ } = props
62
+ const theme = useTheme()
63
63
 
64
- const styles = StyleSheet.create({
65
- mainContainer: {
66
- flex: 1,
67
- marginTop: 64
68
- },
69
- BackIcon: {
70
- paddingRight: 20,
71
- },
72
- headerItem: {
73
- flexDirection: 'row',
74
- alignItems: 'center',
75
- },
76
- btnBackArrow: {
77
- borderWidth: 0,
78
- backgroundColor: theme.colors.clear,
79
- paddingLeft: I18nManager.isRTL ? 7 : 0,
80
- paddingRight: I18nManager.isRTL ? 0 : 7,
81
- },
82
- searchIcon: {
83
- width: 25,
84
- height: 25,
85
- borderWidth: 0,
86
- backgroundColor: theme.colors.white,
87
- borderRadius: 24,
88
- justifyContent: 'center',
89
- alignItems: 'center',
90
- shadowOpacity: 0,
91
- marginEnd: -6
92
- },
93
- categorySticky: {
94
- position: 'absolute',
95
- start: 1,
96
- end: 0,
97
- top: 0
98
- }
99
- })
64
+ const styles = StyleSheet.create({
65
+ mainContainer: {
66
+ flex: 1,
67
+ marginTop: 64
68
+ },
69
+ BackIcon: {
70
+ paddingRight: 20,
71
+ },
72
+ headerItem: {
73
+ flexDirection: 'row',
74
+ alignItems: 'center',
75
+ },
76
+ btnBackArrow: {
77
+ borderWidth: 0,
78
+ backgroundColor: theme.colors.clear,
79
+ paddingLeft: I18nManager.isRTL ? 7 : 0,
80
+ paddingRight: I18nManager.isRTL ? 0 : 7,
81
+ },
82
+ searchIcon: {
83
+ width: 25,
84
+ height: 25,
85
+ borderWidth: 0,
86
+ backgroundColor: theme.colors.white,
87
+ borderRadius: 24,
88
+ justifyContent: 'center',
89
+ alignItems: 'center',
90
+ shadowOpacity: 0,
91
+ marginEnd: -6
92
+ },
93
+ categorySticky: {
94
+ position: 'absolute',
95
+ start: 1,
96
+ end: 0,
97
+ top: 0
98
+ }
99
+ })
100
100
 
101
- const [, t] = useLanguage()
102
- const [{ auth }] = useSession()
103
- const [orderState] = useOrder()
104
- const [{ parsePrice }] = useUtils()
105
- const [ ,{showToast}] = useToast()
106
- const { business, loading, error } = businessState
107
- const [openBusinessInformation, setOpenBusinessInformation] = useState(false)
108
- const [curProduct, setCurProduct] = useState(null)
109
- const [openUpselling, setOpenUpselling] = useState(false)
110
- const [openCart, setOpenCart] = useState(false)
111
- const [isCategoryClicked, setCategoryClicked] = useState(false)
112
- const [categoriesLayout, setCategoriesLayout] = useState<any>({})
113
- const [productListLayout, setProductListLayout] = useState<any>(null)
114
- const [selectedCategoryId, setSelectedCategoryId] = useState<any>('cat_all')
101
+ const [, t] = useLanguage()
102
+ const [{ auth }] = useSession()
103
+ const [orderState] = useOrder()
104
+ const [{ parsePrice }] = useUtils()
105
+ const [, { showToast }] = useToast()
106
+ const { business, loading, error } = businessState
107
+ const [openBusinessInformation, setOpenBusinessInformation] = useState(false)
108
+ const [curProduct, setCurProduct] = useState(null)
109
+ const [openUpselling, setOpenUpselling] = useState(false)
110
+ const [openCart, setOpenCart] = useState(false)
111
+ const [isCategoryClicked, setCategoryClicked] = useState(false)
112
+ const [categoriesLayout, setCategoriesLayout] = useState<any>({})
113
+ const [productListLayout, setProductListLayout] = useState<any>(null)
114
+ const [selectedCategoryId, setSelectedCategoryId] = useState<any>('cat_all')
115
115
 
116
- const scrollViewRef = useRef<any>(null)
116
+ const scrollViewRef = useRef<any>(null)
117
117
 
118
- const { top } = useSafeAreaInsets();
119
- const [sortBy, setSortBy] = useState('alphabet');
118
+ const { top } = useSafeAreaInsets();
119
+ const [sortBy, setSortBy] = useState('alphabet');
120
120
 
121
- const currentCart: any = Object.values(orderState.carts).find((cart: any) => cart?.business?.slug === business?.slug) ?? {}
121
+ const currentCart: any = Object.values(orderState.carts).find((cart: any) => cart?.business?.slug === business?.slug) ?? {}
122
122
 
123
- const onRedirect = (route: string, params?: any) => {
124
- navigation.navigate(route, params)
125
- }
123
+ const onRedirect = (route: string, params?: any) => {
124
+ navigation.navigate(route, params)
125
+ }
126
126
 
127
- const onProductClick = (product: any) => {
128
- setCurProduct(product)
129
- }
127
+ const onProductClick = (product: any) => {
128
+ setCurProduct(product)
129
+ }
130
130
 
131
- const handleCancel = () => {
132
- handleChangeSearch('')
133
- }
131
+ const handleCancel = () => {
132
+ handleChangeSearch('')
133
+ }
134
134
 
135
- const handleCloseProductModal = () => {
136
- setCurProduct(null)
137
- updateProductModal && updateProductModal(null)
138
- }
135
+ const handleCloseProductModal = () => {
136
+ setCurProduct(null)
137
+ updateProductModal && updateProductModal(null)
138
+ }
139
139
 
140
- const handleCloseCartModal = () => {
141
- setOpenCart(false);
142
- }
140
+ const handleCloseCartModal = () => {
141
+ setOpenCart(false);
142
+ }
143
143
 
144
- const handlerProductAction = () => {
145
- handleCloseProductModal()
146
- }
144
+ const handlerProductAction = () => {
145
+ handleCloseProductModal()
146
+ }
147
147
 
148
- const handleUpsellingPage = () => {
149
- onRedirect('CheckoutNavigator', {
150
- screen: 'CheckoutPage',
151
- cartUuid: currentCart?.uuid,
152
- businessLogo: logo,
153
- businessName: business?.name,
154
- cartTotal: currentCart?.total
155
- })
156
- setOpenCart(false)
157
- }
148
+ const handleUpsellingPage = () => {
149
+ onRedirect('CheckoutNavigator', {
150
+ screen: 'CheckoutPage',
151
+ cartUuid: currentCart?.uuid,
152
+ businessLogo: logo,
153
+ businessName: business?.name,
154
+ cartTotal: currentCart?.total
155
+ })
156
+ setOpenCart(false)
157
+ }
158
158
 
159
- const handlePageScroll = useCallback(({ nativeEvent }: any) => {
160
- const scrollOffset = nativeEvent.contentOffset.y
161
- if (businessState?.business?.lazy_load_products_recommended) {
162
- const height = nativeEvent.contentSize.height
163
- const hasMore = !(categoryState.pagination.totalPages === categoryState.pagination.currentPage)
164
- if (scrollOffset + PIXELS_TO_SCROLL > height && !loading && hasMore && getNextProducts) {
165
- getNextProducts()
166
- showToast(ToastType.Info, t('LOADING_MORE_PRODUCTS', 'Loading more products'))
167
- }
168
- } else {
169
- if (!scrollOffset || !categoriesLayout || !productListLayout || isCategoryClicked) return
170
-
171
- for (const key in categoriesLayout) {
172
- const categoryOffset = categoriesLayout[key].y + productListLayout?.y - 70
173
- if (scrollOffset < 10) {
174
- setSelectedCategoryId('cat_all');
175
- return;
176
- }
177
- if (categoryOffset - 50 <= scrollOffset && scrollOffset <= categoryOffset + 50) {
178
- if (selectedCategoryId !== key) {
179
- setSelectedCategoryId(key)
180
- }
181
- }
182
- }
183
- }
184
- }, [isCategoryClicked, selectedCategoryId])
159
+ const handlePageScroll = useCallback(({ nativeEvent }: any) => {
160
+ const scrollOffset = nativeEvent.contentOffset.y
161
+ if (businessState?.business?.lazy_load_products_recommended) {
162
+ const height = nativeEvent.contentSize.height
163
+ const hasMore = !(categoryState.pagination.totalPages === categoryState.pagination.currentPage)
164
+ if (scrollOffset + PIXELS_TO_SCROLL > height && !loading && hasMore && getNextProducts) {
165
+ getNextProducts()
166
+ showToast(ToastType.Info, t('LOADING_MORE_PRODUCTS', 'Loading more products'))
167
+ }
168
+ } else {
169
+ if (!scrollOffset || !categoriesLayout || !productListLayout || isCategoryClicked) return
185
170
 
186
- const handleTouchDrag = useCallback(() => {
187
- setCategoryClicked(false);
188
- }, []);
171
+ for (const key in categoriesLayout) {
172
+ const categoryOffset = categoriesLayout[key].y + productListLayout?.y - 70
173
+ if (scrollOffset < 10) {
174
+ setSelectedCategoryId('cat_all');
175
+ return;
176
+ }
177
+ if (categoryOffset - 50 <= scrollOffset && scrollOffset <= categoryOffset + 50) {
178
+ if (selectedCategoryId !== key) {
179
+ setSelectedCategoryId(key)
180
+ }
181
+ }
182
+ }
183
+ }
184
+ }, [isCategoryClicked, selectedCategoryId, productListLayout])
189
185
 
190
- return (
191
- <>
192
- <Animated.View style={{ flex: 1, backgroundColor: theme.colors.white, position: 'absolute', width: '100%', top: top, zIndex: 1 }}>
193
- {!loading && business?.id && (
194
- <TopHeader>
195
- <OButton
196
- imgLeftSrc={theme.images.general.arrow_left}
197
- imgRightSrc={null}
198
- style={styles.btnBackArrow}
199
- onClick={() => (navigation?.canGoBack() && navigation.goBack()) || (auth && navigation.navigate('BottomTab'))}
200
- imgLeftStyle={{ tintColor: theme.colors.textPrimary }}
201
- />
202
- {!errorQuantityProducts && (
203
- <View style={{ ...styles.headerItem }}>
204
- <View
205
- style={styles.searchIcon}
206
- >
207
- <SocialShareFav
208
- icon={theme.images.general.share}
209
- style={{width: 19, height: 19}}
210
- />
211
- </View>
212
- </View>
213
- )}
214
- </TopHeader>
215
- )}
216
- </Animated.View>
217
- <BusinessProductsListingContainer
218
- style={styles.mainContainer}
219
- isActiveFloatingButtom={currentCart?.products?.length > 0 && categoryState.products.length !== 0}
220
- stickyHeaderIndices={[1]}
221
- ref={scrollViewRef}
222
- onScroll={handlePageScroll}
223
- onScrollBeginDrag={handleTouchDrag}
224
- scrollEventThrottle={16}
225
- >
226
- <WrapHeader>
227
- <BusinessBasicInformation
228
- businessState={businessState}
229
- openBusinessInformation={openBusinessInformation}
230
- header={header}
231
- logo={logo}
232
- noImage={true}
233
- />
234
- <View style={{ display: 'flex', position: 'relative', flexDirection: 'row', minHeight: 42, paddingHorizontal: 40, marginTop: 12 }}>
235
- <WrapSearchBar>
236
- <SearchBar
237
- onSearch={handleChangeSearch}
238
- isCancelXButtonShow
239
- noBorderShow
240
- placeholder={t('SEARCH', 'Search')}
241
- lazyLoad={businessState?.business?.lazy_load_products_recommended}
242
- inputWrapStyle={{ height: 40, backgroundColor: theme.colors.clear, borderWidth: 0, paddingStart: 11, marginEnd: 0 }}
243
- />
244
- </WrapSearchBar>
245
- <SortWrap>
246
- <SortButton onPress={() => setSortBy('rank')} style={{ marginEnd: 7 }}>
247
- <OText size={12} weight={Platform.OS == 'ios' ? '600' : 'bold'} color={theme.colors.textPrimary}>{t('RANK', 'Rank')}</OText>
248
- </SortButton>
249
- <SortButton onPress={() => setSortBy('alphabet')}>
250
- <OText size={12} weight={Platform.OS == 'ios' ? '600' : 'bold'} color={theme.colors.textPrimary}>{t('A_TO_Z', 'A to Z')}</OText>
251
- </SortButton>
252
- </SortWrap>
253
- </View>
254
- </WrapHeader>
255
- {!loading && business?.id && !(business?.categories?.length === 0) && (
256
- <CategoryWrap>
257
- <BusinessProductsCategories
258
- categories={[{ id: 'all', name: t('ALL', 'All') }, { id: 'featured', name: t('FEATURED', 'Featured') }, ...business?.categories.sort((a: any, b: any) => a.rank - b.rank)]}
259
- categorySelected={categorySelected}
260
- onClickCategory={handleChangeCategory}
261
- featured={featuredProducts}
262
- openBusinessInformation={openBusinessInformation}
263
- scrollViewRef={scrollViewRef}
264
- contentStyle={{ paddingHorizontal: 40 }}
265
- productListLayout={productListLayout}
266
- categoriesLayout={categoriesLayout}
267
- selectedCategoryId={selectedCategoryId}
268
- setSelectedCategoryId={setSelectedCategoryId}
269
- setCategoryClicked={setCategoryClicked}
270
- lazyLoadProductsRecommended={business?.lazy_load_products_recommended}
271
- />
272
- </CategoryWrap>
273
- )}
274
- <View>
275
- {!loading && business?.id && (
276
- <WrapContent onLayout={(event: any) => setProductListLayout(event.nativeEvent.layout)}>
277
- <BusinessProductsList
278
- categories={[
279
- { id: null, name: t('ALL', 'All') },
280
- { id: 'featured', name: t('FEATURED', 'Featured') },
281
- ...business?.categories.sort((a: any, b: any) => a.rank - b.rank)
282
- ]}
283
- category={categorySelected}
284
- categoryState={categoryState}
285
- businessId={business.id}
286
- errors={errors}
287
- onProductClick={onProductClick}
288
- handleSearchRedirect={handleSearchRedirect}
289
- featured={featuredProducts}
290
- searchValue={searchValue}
291
- handleClearSearch={handleChangeSearch}
292
- errorQuantityProducts={errorQuantityProducts}
293
- handleCancelSearch={handleCancel}
294
- sortBy={sortBy}
295
- categoriesLayout={categoriesLayout}
296
- setCategoriesLayout={setCategoriesLayout}
297
- />
298
- </WrapContent>
299
- )}
300
- {loading && !error && (
301
- <>
302
- <BusinessProductsCategories
303
- categories={[]}
304
- categorySelected={categorySelected}
305
- onClickCategory={handleChangeCategory}
306
- featured={featuredProducts}
307
- openBusinessInformation={openBusinessInformation}
308
- loading={loading}
309
- />
310
- <WrapContent>
311
- <BusinessProductsList
312
- categories={[]}
313
- category={categorySelected}
314
- categoryState={categoryState}
315
- isBusinessLoading={loading}
316
- errorQuantityProducts={errorQuantityProducts}
317
- />
318
- </WrapContent>
319
- </>
320
- )}
321
- </View>
322
- </BusinessProductsListingContainer>
323
- {!loading && auth && currentCart?.products?.length > 0 && categoryState.products.length !== 0 && !openCart && curProduct === null && (
324
- <FloatingButton
325
- btnText={
326
- currentCart?.subtotal >= currentCart?.minimum
327
- ? !openUpselling ? t('VIEW_CART', 'View Cart') : t('LOADING', 'Loading')
328
- : `${t('MINIMUN_SUBTOTAL_ORDER', 'Minimum subtotal order:')} ${parsePrice(currentCart?.minimum)}`
329
- }
330
- isSecondaryBtn={currentCart?.subtotal < currentCart?.minimum}
331
- // btnLeftValueShow={currentCart?.subtotal >= currentCart?.minimum && !openUpselling && currentCart?.products?.length > 0}
332
- // btnRightValueShow={currentCart?.subtotal >= currentCart?.minimum && !openUpselling && currentCart?.products?.length > 0}
333
- btnLeftValue={currentCart?.products?.length}
334
- btnRightValue={parsePrice(currentCart?.total)}
335
- disabled={openUpselling || currentCart?.subtotal < currentCart?.minimum}
336
- handleClick={() => setOpenCart(true)}
337
- />
338
- )}
339
- <OModal
340
- open={!!curProduct || (!!productModal.product && !orderState.loading)}
341
- onClose={handleCloseProductModal}
342
- entireModal
343
- customClose
344
- >
345
- <ProductForm
346
- product={curProduct || productModal.product}
347
- businessSlug={business?.slug}
348
- businessId={business?.id || productModal?.product?.category?.business_id}
349
- onClose={handleCloseProductModal}
350
- navigation={navigation}
351
- onSave={handlerProductAction}
352
- setProductLogin={setProductLogin}
353
- />
354
- </OModal>
355
- <OModal
356
- open={openCart}
357
- onClose={handleCloseCartModal}
358
- entireModal
359
- customClose
360
- >
361
- <ScrollView stickyHeaderIndices={[0]} style={{backgroundColor: 'white'}} contentContainerStyle={{ paddingBottom: 100 }}>
362
- <NavBar title={t('CART', 'Cart')} onActionLeft={handleCloseCartModal} leftImg={theme.images.general.close} noBorder btnStyle={{paddingLeft: 0}} />
363
- <OrderSummary
364
- cart={currentCart}
365
- isCartPending={currentCart?.status === 2}
366
- hasUpSelling={true}
367
- isFromCheckout
368
- title={t('ITEMS', 'Items')}
369
- paddingH={40}
370
- />
371
- </ScrollView>
372
- <FloatingButton btnText={t('CHECKOUT', 'Checkout')} handleClick={() => handleUpsellingPage()} />
373
- </OModal>
374
- </>
375
- )
186
+ const handleTouchDrag = useCallback(() => {
187
+ setCategoryClicked(false);
188
+ }, []);
189
+
190
+ return (
191
+ <>
192
+ <Animated.View style={{ flex: 1, backgroundColor: theme.colors.white, position: 'absolute', width: '100%', top: top, zIndex: 1 }}>
193
+ {!loading && business?.id && (
194
+ <TopHeader>
195
+ <OButton
196
+ imgLeftSrc={theme.images.general.arrow_left}
197
+ imgRightSrc={null}
198
+ style={styles.btnBackArrow}
199
+ onClick={() => (navigation?.canGoBack() && navigation.goBack()) || (auth && navigation.navigate('BottomTab'))}
200
+ imgLeftStyle={{ tintColor: theme.colors.textPrimary }}
201
+ />
202
+ {!errorQuantityProducts && (
203
+ <View style={{ ...styles.headerItem }}>
204
+ <View
205
+ style={styles.searchIcon}
206
+ >
207
+ <SocialShareFav
208
+ icon={theme.images.general.share}
209
+ style={{ width: 19, height: 19 }}
210
+ />
211
+ </View>
212
+ </View>
213
+ )}
214
+ </TopHeader>
215
+ )}
216
+ </Animated.View>
217
+ <BusinessProductsListingContainer
218
+ style={styles.mainContainer}
219
+ isActiveFloatingButtom={currentCart?.products?.length > 0 && categoryState.products.length !== 0}
220
+ stickyHeaderIndices={[1]}
221
+ ref={scrollViewRef}
222
+ onScroll={handlePageScroll}
223
+ onScrollBeginDrag={handleTouchDrag}
224
+ scrollEventThrottle={16}
225
+ >
226
+ <WrapHeader>
227
+ <BusinessBasicInformation
228
+ businessState={businessState}
229
+ openBusinessInformation={openBusinessInformation}
230
+ header={header}
231
+ logo={logo}
232
+ noImage={true}
233
+ />
234
+ <View style={{ display: 'flex', position: 'relative', flexDirection: 'row', minHeight: 42, paddingHorizontal: 40, marginTop: 12 }}>
235
+ <WrapSearchBar>
236
+ <SearchBar
237
+ onSearch={handleChangeSearch}
238
+ isCancelXButtonShow
239
+ noBorderShow
240
+ placeholder={t('SEARCH', 'Search')}
241
+ lazyLoad={businessState?.business?.lazy_load_products_recommended}
242
+ inputWrapStyle={{ height: 40, backgroundColor: theme.colors.clear, borderWidth: 0, paddingStart: 11, marginEnd: 0 }}
243
+ />
244
+ </WrapSearchBar>
245
+ <SortWrap>
246
+ <SortButton onPress={() => setSortBy('rank')} style={{ marginEnd: 7 }}>
247
+ <OText size={12} weight={Platform.OS == 'ios' ? '600' : 'bold'} color={theme.colors.textPrimary}>{t('RANK', 'Rank')}</OText>
248
+ </SortButton>
249
+ <SortButton onPress={() => setSortBy('alphabet')}>
250
+ <OText size={12} weight={Platform.OS == 'ios' ? '600' : 'bold'} color={theme.colors.textPrimary}>{t('A_TO_Z', 'A to Z')}</OText>
251
+ </SortButton>
252
+ </SortWrap>
253
+ </View>
254
+ </WrapHeader>
255
+ {!loading && business?.id && !(business?.categories?.length === 0) && (
256
+ <CategoryWrap>
257
+ <BusinessProductsCategories
258
+ categories={[{ id: 'all', name: t('ALL', 'All') }, { id: 'featured', name: t('FEATURED', 'Featured') }, ...business?.categories.sort((a: any, b: any) => a.rank - b.rank)]}
259
+ categorySelected={categorySelected}
260
+ onClickCategory={handleChangeCategory}
261
+ featured={featuredProducts}
262
+ openBusinessInformation={openBusinessInformation}
263
+ scrollViewRef={scrollViewRef}
264
+ contentStyle={{ paddingHorizontal: 40 }}
265
+ productListLayout={productListLayout}
266
+ categoriesLayout={categoriesLayout}
267
+ selectedCategoryId={selectedCategoryId}
268
+ setSelectedCategoryId={setSelectedCategoryId}
269
+ setCategoryClicked={setCategoryClicked}
270
+ lazyLoadProductsRecommended={business?.lazy_load_products_recommended}
271
+ />
272
+ </CategoryWrap>
273
+ )}
274
+ <View>
275
+ {!loading && business?.id && (
276
+ <WrapContent onLayout={(event: any) => setProductListLayout(event.nativeEvent.layout)}>
277
+ <BusinessProductsList
278
+ categories={[
279
+ { id: null, name: t('ALL', 'All') },
280
+ { id: 'featured', name: t('FEATURED', 'Featured') },
281
+ ...business?.categories.sort((a: any, b: any) => a.rank - b.rank)
282
+ ]}
283
+ category={categorySelected}
284
+ categoryState={categoryState}
285
+ businessId={business.id}
286
+ errors={errors}
287
+ onProductClick={onProductClick}
288
+ handleSearchRedirect={handleSearchRedirect}
289
+ featured={featuredProducts}
290
+ searchValue={searchValue}
291
+ handleClearSearch={handleChangeSearch}
292
+ errorQuantityProducts={errorQuantityProducts}
293
+ handleCancelSearch={handleCancel}
294
+ sortBy={sortBy}
295
+ categoriesLayout={categoriesLayout}
296
+ setCategoriesLayout={setCategoriesLayout}
297
+ />
298
+ </WrapContent>
299
+ )}
300
+ {loading && !error && (
301
+ <>
302
+ <BusinessProductsCategories
303
+ categories={[]}
304
+ categorySelected={categorySelected}
305
+ onClickCategory={handleChangeCategory}
306
+ featured={featuredProducts}
307
+ openBusinessInformation={openBusinessInformation}
308
+ loading={loading}
309
+ />
310
+ <WrapContent>
311
+ <BusinessProductsList
312
+ categories={[]}
313
+ category={categorySelected}
314
+ categoryState={categoryState}
315
+ isBusinessLoading={loading}
316
+ errorQuantityProducts={errorQuantityProducts}
317
+ />
318
+ </WrapContent>
319
+ </>
320
+ )}
321
+ </View>
322
+ </BusinessProductsListingContainer>
323
+ {!loading && auth && currentCart?.products?.length > 0 && categoryState.products.length !== 0 && !openCart && curProduct === null && (
324
+ <FloatingButton
325
+ btnText={
326
+ currentCart?.subtotal >= currentCart?.minimum
327
+ ? !openUpselling ? t('VIEW_CART', 'View Cart') : t('LOADING', 'Loading')
328
+ : `${t('MINIMUN_SUBTOTAL_ORDER', 'Minimum subtotal order:')} ${parsePrice(currentCart?.minimum)}`
329
+ }
330
+ isSecondaryBtn={currentCart?.subtotal < currentCart?.minimum}
331
+ // btnLeftValueShow={currentCart?.subtotal >= currentCart?.minimum && !openUpselling && currentCart?.products?.length > 0}
332
+ // btnRightValueShow={currentCart?.subtotal >= currentCart?.minimum && !openUpselling && currentCart?.products?.length > 0}
333
+ btnLeftValue={currentCart?.products?.length}
334
+ btnRightValue={parsePrice(currentCart?.total)}
335
+ disabled={openUpselling || currentCart?.subtotal < currentCart?.minimum}
336
+ handleClick={() => setOpenCart(true)}
337
+ />
338
+ )}
339
+ <OModal
340
+ open={!!curProduct || (!!productModal.product && !orderState.loading)}
341
+ onClose={handleCloseProductModal}
342
+ entireModal
343
+ customClose
344
+ >
345
+ <ProductForm
346
+ product={curProduct || productModal.product}
347
+ businessSlug={business?.slug}
348
+ businessId={business?.id || productModal?.product?.category?.business_id}
349
+ onClose={handleCloseProductModal}
350
+ navigation={navigation}
351
+ onSave={handlerProductAction}
352
+ setProductLogin={setProductLogin}
353
+ />
354
+ </OModal>
355
+ <OModal
356
+ open={openCart}
357
+ onClose={handleCloseCartModal}
358
+ entireModal
359
+ customClose
360
+ >
361
+ <ScrollView stickyHeaderIndices={[0]} style={{ backgroundColor: 'white' }} contentContainerStyle={{ paddingBottom: 100 }}>
362
+ <NavBar title={t('CART', 'Cart')} onActionLeft={handleCloseCartModal} leftImg={theme.images.general.close} noBorder btnStyle={{ paddingLeft: 0 }} />
363
+ <OrderSummary
364
+ cart={currentCart}
365
+ isCartPending={currentCart?.status === 2}
366
+ hasUpSelling={true}
367
+ isFromCheckout
368
+ title={t('ITEMS', 'Items')}
369
+ paddingH={40}
370
+ />
371
+ </ScrollView>
372
+ <FloatingButton btnText={t('CHECKOUT', 'Checkout')} handleClick={() => handleUpsellingPage()} />
373
+ </OModal>
374
+ </>
375
+ )
376
376
  }
377
377
 
378
378
  export const BusinessProductsListing = (props: BusinessProductsListingParams) => {
379
- const businessProductslistingProps = {
380
- ...props,
381
- UIComponent: BusinessProductsListingUI
382
- }
383
- return (
384
- <BusinessAndProductList {...businessProductslistingProps} />
385
- )
379
+ const businessProductslistingProps = {
380
+ ...props,
381
+ UIComponent: BusinessProductsListingUI
382
+ }
383
+ return (
384
+ <BusinessAndProductList {...businessProductslistingProps} />
385
+ )
386
386
  }
@@ -1,5 +1,6 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { Fade, Placeholder, PlaceholderLine } from 'rn-placeholder';
3
+ import Geolocation from '@react-native-community/geolocation'
3
4
  import {
4
5
  View,
5
6
  StyleSheet,
@@ -16,6 +17,7 @@ import {
16
17
  useUtils,
17
18
  } from 'ordering-components/native';
18
19
  import { useTheme } from 'styled-components/native';
20
+ import Ionicons from 'react-native-vector-icons/Ionicons'
19
21
 
20
22
  import {
21
23
  Search,
@@ -25,7 +27,8 @@ import {
25
27
  HeaderWrapper,
26
28
  ListWrapper,
27
29
  FeaturedWrapper,
28
- OrderProgressWrapper
30
+ OrderProgressWrapper,
31
+ FarAwayMessage
29
32
  } from './styles';
30
33
 
31
34
  import { SearchBar } from '../SearchBar';
@@ -38,7 +41,7 @@ import { OrderTypeSelector } from '../OrderTypeSelector';
38
41
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
39
42
  import { BusinessFeaturedController } from '../BusinessFeaturedController';
40
43
  import { HighestRatedBusinesses } from '../HighestRatedBusinesses';
41
- import { getTypesText } from '../../utils';
44
+ import { getTypesText, convertToRadian } from '../../utils';
42
45
  import { OrderProgress } from '../OrderProgress';
43
46
  import { useIsFocused } from '@react-navigation/native';
44
47
 
@@ -85,6 +88,15 @@ const BusinessesListingUI = (props: BusinessesListingParams) => {
85
88
  },
86
89
  searchInput: {
87
90
  fontSize: 12,
91
+ },
92
+ iconStyle: {
93
+ fontSize: 18,
94
+ color: theme.colors.warning5,
95
+ marginRight: 8
96
+ },
97
+ farAwayMsg: {
98
+ paddingVertical: 6,
99
+ paddingHorizontal: 20
88
100
  }
89
101
  });
90
102
 
@@ -98,6 +110,7 @@ const BusinessesListingUI = (props: BusinessesListingParams) => {
98
110
  const { top } = useSafeAreaInsets();
99
111
 
100
112
  const [featuredBusiness, setFeaturedBusinesses] = useState(Array);
113
+ const [isFarAway, setIsFarAway] = useState(false)
101
114
 
102
115
  // const timerId = useRef<any>(false)
103
116
  // const panResponder = useRef(
@@ -127,6 +140,17 @@ const BusinessesListingUI = (props: BusinessesListingParams) => {
127
140
  }
128
141
  };
129
142
 
143
+ const getDistance = (lat1: any, lon1: any, lat2: any, lon2: any) => {
144
+ const R = 6371 // km
145
+ const dLat = convertToRadian(lat2 - lat1)
146
+ const dLon = convertToRadian(lon2 - lon1)
147
+ const curLat1 = convertToRadian(lat1)
148
+ const curLat2 = convertToRadian(lat2)
149
+ const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(curLat1) * Math.cos(curLat2)
150
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
151
+ return R * c
152
+ }
153
+
130
154
  useEffect(() => {
131
155
  if (businessesList.businesses.length > 0) {
132
156
  const fb = businessesList.businesses.filter((b) => b.featured == true);
@@ -148,6 +172,19 @@ const BusinessesListingUI = (props: BusinessesListingParams) => {
148
172
  // resetInactivityTimeout()
149
173
  // }, [])
150
174
 
175
+ useEffect(() => {
176
+ Geolocation.getCurrentPosition((pos) => {
177
+ const crd = pos.coords
178
+ const distance = getDistance(crd.latitude, crd.longitude, orderState?.options?.address?.location?.lat, orderState?.options?.address?.location?.lng)
179
+ if (distance > 20) setIsFarAway(true)
180
+ else setIsFarAway(false)
181
+ }, (err) => {
182
+ console.log(`ERROR(${err.code}): ${err.message}`)
183
+ }, {
184
+ enableHighAccuracy: true, timeout: 15000, maximumAge: 10000
185
+ })
186
+ }, [orderState?.options?.address?.location])
187
+
151
188
  return (
152
189
  <ScrollView style={styles.container} onScroll={(e) => handleScroll(e)} showsVerticalScrollIndicator={false}>
153
190
  <HeaderWrapper
@@ -180,6 +217,12 @@ const BusinessesListingUI = (props: BusinessesListingParams) => {
180
217
  </OText>
181
218
  </AddressInput>
182
219
  </Search>
220
+ {isFarAway && (
221
+ <FarAwayMessage style={styles.farAwayMsg}>
222
+ <Ionicons name='md-warning-outline' style={styles.iconStyle} />
223
+ <OText size={12} numberOfLines={1} ellipsizeMode={'tail'} color={theme.colors.textNormal}>{t('YOU_ARE_FAR_FROM_ADDRESS', 'Your are far from this address')}</OText>
224
+ </FarAwayMessage>
225
+ )}
183
226
  <OrderControlContainer>
184
227
  <View style={styles.wrapperOrderOptions}>
185
228
  <WrapMomentOption onPress={() => navigation.navigate('OrderTypes', { configTypes: configTypes })}>
@@ -73,4 +73,13 @@ export const OrderProgressWrapper = styled.View`
73
73
  margin-top: 37px;
74
74
  margin-bottom: 20px;
75
75
  padding-horizontal: 40px;
76
- `
76
+ `
77
+
78
+ export const FarAwayMessage = styled.View`
79
+ flex-direction: row;
80
+ align-items: center;
81
+ background-color: ${(props: any) => props.theme.colors.warning1};
82
+ margin-bottom: 25px;
83
+ border-radius: 7.6px;
84
+ border: 1px solid ${(props: any) => props.theme.colors.warning5};
85
+ `
@@ -168,4 +168,13 @@ export const transformCountryCode = (countryCode : number) => {
168
168
  export const getTypesText = (value: number) => {
169
169
  const ret = ORDER_TYPES.find((type: any) => type.value == value);
170
170
  return ret?.content;
171
+ }
172
+
173
+ /**
174
+ * Function to transform degree to radian
175
+ * @param {number} value for transform
176
+ *
177
+ */
178
+ export const convertToRadian = (value: number) => {
179
+ return value * Math.PI / 180
171
180
  }
@@ -41,10 +41,11 @@ const DriverTipsUI = (props: any) => {
41
41
  borderRadius: 30
42
42
  },
43
43
  inputStyle: {
44
+ borderColor: theme.colors.border,
45
+ borderRadius: 8,
46
+ marginRight: 20,
47
+ height: 44,
44
48
  flex: 1,
45
- borderWidth: 1,
46
- borderColor: theme.colors.disabled,
47
- marginRight: 10
48
49
  }
49
50
  })
50
51
 
@@ -117,21 +118,23 @@ const DriverTipsUI = (props: any) => {
117
118
  <OInput
118
119
  placeholder={placeholderCurrency}
119
120
  style={style.inputStyle}
121
+ inputStyle={{ fontSize: 12 }}
120
122
  onChange={handleChangeDriverTip}
121
123
  autoCapitalize='none'
122
124
  autoCorrect={false}
123
125
  />
124
126
  <OButton
125
- text={t('APPLY_TIP', 'Apply Tip')}
127
+ text={t('APPLY', 'Apply')}
126
128
  bgColor={theme.colors.primary}
127
129
  borderColor={theme.colors.primary}
128
- textStyle={{ color: 'white', fontSize: 20 }}
130
+ textStyle={{ color: 'white', fontSize: 12 }}
129
131
  imgRightSrc={null}
130
132
  isDisabled={!(value > 0 && value !== driverTip) || !value}
131
133
  onClick={() => {
132
134
  handlerChangeOption(value)
133
135
  setvalue(0)
134
136
  }}
137
+ style={{ borderRadius: 8, height: 44, shadowOpacity: 0 }}
135
138
  />
136
139
  </DTWrapperInput>
137
140
  {parseFloat(driverTip || 0) > 0 && (
@@ -60,5 +60,4 @@ export const DTWrapperInput = styled.View`
60
60
  flex-direction: row;
61
61
  align-items: center;
62
62
  justify-content: center;
63
- margin-top: 20px;
64
63
  `
@@ -192,19 +192,19 @@ const OrderSummaryUI = (props: any) => {
192
192
  {cart?.status !== 2 && (
193
193
  <OSTable>
194
194
  <View style={{ width: '100%', marginTop: 20 }}>
195
- <OText size={12}>{t('COMMENTS', 'Comments')}</OText>
195
+ <OText size={12} style={{ marginBottom: 10 }}>{t('COMMENTS', 'Comments')}</OText>
196
196
  <View style={{ flex: 1, width: '100%' }}>
197
197
  <OInput
198
198
  value={cart?.comment}
199
199
  placeholder={t('SPECIAL_COMMENTS', 'Special Comments')}
200
200
  onChange={(value: string) => handleChangeComment(value)}
201
201
  style={{
202
+ borderColor: theme.colors.border,
203
+ borderRadius: 10,
204
+ marginBottom: 20,
205
+ height: 104,
206
+ maxHeight: 104,
202
207
  alignItems: 'flex-start',
203
- width: '100%',
204
- height: 100,
205
- borderColor: theme.colors.textSecondary,
206
- paddingRight: 50,
207
- marginTop: 10
208
208
  }}
209
209
  multiline
210
210
  inputStyle={{ color: theme.colors.textPrimary, fontSize: 12 }}