ordering-ui-react-native 0.15.88 → 0.15.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/package.json +1 -1
  2. package/src/utils/index.tsx +28 -28
  3. package/themes/business/src/components/AcceptOrRejectOrder/index.tsx +100 -60
  4. package/themes/business/src/components/AcceptOrRejectOrder/styles.tsx +1 -0
  5. package/themes/business/src/components/Chat/index.tsx +38 -86
  6. package/themes/business/src/components/LoginForm/index.tsx +87 -2
  7. package/themes/business/src/components/LoginForm/styles.tsx +6 -0
  8. package/themes/business/src/components/NewOrderNotification/index.tsx +4 -2
  9. package/themes/business/src/components/OrderDetails/Delivery.tsx +11 -3
  10. package/themes/business/src/components/OrdersListManager/index.tsx +1 -1
  11. package/themes/business/src/components/OrdersOption/index.tsx +1 -1
  12. package/themes/business/src/types/index.tsx +3 -0
  13. package/themes/original/src/components/AppleLogin/index.tsx +2 -4
  14. package/themes/original/src/components/BusinessListingSearch/index.tsx +65 -7
  15. package/themes/original/src/components/BusinessListingSearch/styles.tsx +10 -1
  16. package/themes/original/src/components/Cart/index.tsx +1 -1
  17. package/themes/original/src/components/Checkout/index.tsx +6 -2
  18. package/themes/original/src/components/LoginForm/Otp/index.tsx +0 -1
  19. package/themes/original/src/components/LoginForm/index.tsx +2 -2
  20. package/themes/original/src/components/LoginForm/styles.tsx +1 -3
  21. package/themes/original/src/components/ProductForm/index.tsx +48 -40
  22. package/themes/original/src/components/ProductOptionSubOption/index.tsx +13 -9
  23. package/themes/original/src/components/UserProfile/index.tsx +1 -1
  24. package/themes/original/src/components/Wallets/index.tsx +1 -1
  25. package/themes/original/src/types/index.tsx +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ordering-ui-react-native",
3
- "version": "0.15.88",
3
+ "version": "0.15.91",
4
4
  "description": "Reusable components made in react native",
5
5
  "main": "src/index.tsx",
6
6
  "author": "ordering.inc",
@@ -408,49 +408,49 @@ export const orderCommentList = (value: string) => {
408
408
 
409
409
  const messages: any = {
410
410
  6: [// on reject order
411
- 'Very far away',
412
- 'Driver/ vehicle incident',
413
- 'Destination unreachable',
414
- 'Unavailable driver',
415
- 'Other'
411
+ 'very_far_away',
412
+ 'driver_vehicle_incident',
413
+ 'destination_unreacheable',
414
+ 'unavailable_driver',
415
+ 'other'
416
416
  ],
417
417
  9: [// on force pickup status
418
- 'I forgot to complete it in the location',
419
- 'I didn\'t have internet connection',
420
- 'Other'
418
+ 'forgot_complete_location',
419
+ 'not_internet_conection',
420
+ 'other'
421
421
  ],
422
422
  10: [// on pickup failed by driver
423
- 'Very far away',
424
- 'Driver/ vehicle incident',
425
- 'Destination unreachable',
426
- 'Store closed',
427
- 'Unavailable driver',
428
- 'Other'
423
+ 'very_far_away',
424
+ 'driver_vehicle_incident',
425
+ 'destination_unreacheable',
426
+ 'store_closed',
427
+ 'unavailable_driver',
428
+ 'other'
429
429
  ],
430
430
  11: [// on force delivery status
431
- 'I forgot to complete it in the location',
432
- 'I didn\'t have internet connection',
433
- 'Other'
431
+ 'forgot_complete_location',
432
+ 'not_internet_conection',
433
+ 'other'
434
434
  ],
435
435
  12: [// on delivery failed by driver
436
- 'Very far away',
437
- 'Driver/ vehicle incident',
438
- 'Destination unreachable',
439
- 'Recipient unavailable',
440
- 'Refused- incorrect/ missing items',
441
- 'Refused- damage',
442
- 'Other'
436
+ 'very_far_away',
437
+ 'driver_vehicle_incident',
438
+ 'destination_unreacheable',
439
+ 'recipient_unavailable',
440
+ 'incorrect_missing_items',
441
+ 'refused_damage',
442
+ 'other'
443
443
  ],
444
444
  14: [// on order not ready
445
- 'Store didn\'t receive the order on time',
446
- 'Store very busy',
447
- 'Other'
445
+ 'store_recieve_order_late',
446
+ 'store_busy',
447
+ 'other'
448
448
  ]
449
449
  }
450
450
 
451
451
  if (!messages[status]) return null
452
452
 
453
- const list = messages[status].map((val: any, i: number) => ({ key: i, content: t(`ORDER_STATUS_${status}_QUICK_COMMENT_${i}`, val) }))
453
+ const list = messages[status].map((val: any, i: number) => ({ key: i, value: val, content: t(`REJECT_REASON_${val.toUpperCase()}`, val.replaceAll('_', ' ')) }))
454
454
 
455
455
  return { list }
456
456
  }
@@ -1,14 +1,17 @@
1
1
  import React, { useState, useEffect, useRef } from 'react';
2
+ import FeatherIcon from 'react-native-vector-icons/Feather';
2
3
  import {
3
4
  Linking,
4
5
  Keyboard,
5
6
  Platform,
6
7
  View,
7
8
  KeyboardAvoidingView,
8
- TextInput
9
+ TextInput,
10
+ StyleSheet
9
11
  } from 'react-native';
10
12
  import { useTheme } from 'styled-components/native';
11
- import { useLanguage } from 'ordering-components/native';
13
+ import SelectDropdown from 'react-native-select-dropdown'
14
+ import { useLanguage, useSession } from 'ordering-components/native';
12
15
  import { Content, Timer, TimeField, Header, Action, Comments, CommentsButtonGroup } from './styles';
13
16
  import { FloatingButton } from '../FloatingButton';
14
17
  import { OText, OButton, OTextarea, OIconButton } from '../shared';
@@ -32,6 +35,7 @@ export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
32
35
  } = props;
33
36
 
34
37
  const [, t] = useLanguage();
38
+ const [{ user }] = useSession();
35
39
  const theme = useTheme();
36
40
  const scrollViewRef = useRef<any>(null);
37
41
  const viewRef = useRef<any>(null);
@@ -41,10 +45,12 @@ export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
41
45
  const [min, setMin] = useState('00');
42
46
  const [time, setTime] = useState('');
43
47
  const [comments, setComments] = useState('');
44
- const [commentList, setCommentList] = useState<any>([]);
48
+ const [rejectReason, setRejectReason] = useState(null);
45
49
  const [isKeyboardShow, setIsKeyboardShow] = useState(false);
46
50
  const { top, bottom } = useSafeAreaInsets()
47
51
 
52
+ const isDriverApp = appTitle?.key === 'DELIVERY_APP'
53
+
48
54
  const orderCommentsList = orderCommentList(action)
49
55
 
50
56
  let codeNumberPhone, numberPhone, numberToShow;
@@ -53,20 +59,41 @@ export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
53
59
  const buttonText = t(orderTitle[action]?.btnKey, orderTitle[action]?.btnText)
54
60
  const showTextArea = ['reject', 'deliveryFailed', 'pickupFailed', 'notReady', 'forcePickUp', 'forceDelivery'].includes(action)
55
61
 
56
- const isSelectedComment = (commentKey: number) => {
57
- const found = commentList.find((comment: any) => comment?.key === commentKey)
58
- return found
59
- }
60
-
61
- const handleChangeComments = (commentItem: any) => {
62
- const found = commentList.find((comment: any) => comment?.key === commentItem.key)
63
- if (found) {
64
- const _comments = commentList.filter((comment: any) => comment?.key !== commentItem.key)
65
- setCommentList(_comments)
66
- } else {
67
- setCommentList([...commentList, commentItem])
68
- }
69
- }
62
+ const styles = StyleSheet.create({
63
+ selectOption: {
64
+ alignItems: 'center',
65
+ justifyContent: 'space-between',
66
+ minHeight: 40,
67
+ width: '100%',
68
+ paddingHorizontal: 15,
69
+ backgroundColor: theme.colors.inputChat,
70
+ borderRadius: 7.6,
71
+ },
72
+ buttonTextStyle: {
73
+ textAlign: 'left',
74
+ marginHorizontal: 0,
75
+ fontSize: 16,
76
+ lineHeight: 24,
77
+ color: '#748194',
78
+ textTransform: 'capitalize'
79
+ },
80
+ dropdownStyle: {
81
+ borderWidth: 1,
82
+ borderRadius: 8,
83
+ paddingTop: 5,
84
+ backgroundColor: '#fff',
85
+ borderColor: theme.colors.lightGray,
86
+ overflow: 'hidden',
87
+ minHeight: 155
88
+ },
89
+ rowStyle: {
90
+ display: 'flex',
91
+ borderBottomWidth: 0,
92
+ height: 36,
93
+ alignItems: 'center',
94
+ paddingHorizontal: 10
95
+ },
96
+ })
70
97
 
71
98
  const handleFocus = () => {
72
99
  viewRef?.current?.measure((x: any, y: any) => {
@@ -74,12 +101,6 @@ export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
74
101
  });
75
102
  };
76
103
 
77
- const handleFocusTimer = () => {
78
- timerRef?.current?.measure((x: any, y: any) => {
79
- scrollViewRef?.current?.scrollTo({ x: 0, y });
80
- });
81
- };
82
-
83
104
  useEffect(() => {
84
105
  const keyboardDidShowListener = Keyboard.addListener(
85
106
  'keyboardDidShow',
@@ -171,13 +192,6 @@ export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
171
192
 
172
193
  const handleAcceptOrReject = () => {
173
194
  handleFixTime();
174
-
175
- let _comments = ''
176
- if (commentList.length > 0) {
177
- commentList.map((comment: any) => (_comments += comment.content + '. '))
178
- }
179
- const _comment = _comments + comments
180
-
181
195
  let minsToSend = min;
182
196
 
183
197
  if (min > '60') minsToSend = '59';
@@ -191,7 +205,7 @@ export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
191
205
  status: 7,
192
206
  },
193
207
  rejectByBusiness: {
194
- comment: _comment,
208
+ comment: comments,
195
209
  status: 5,
196
210
  },
197
211
  acceptByDriver: {
@@ -199,28 +213,34 @@ export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
199
213
  status: 8,
200
214
  },
201
215
  rejectByDriver: {
202
- comment: _comment,
216
+ comment: comments,
203
217
  status: 6,
218
+ reject_reason: rejectReason
204
219
  },
205
220
  pickupFailedByDriver: {
206
- comment: _comment,
207
- status: 10
221
+ comment: comments,
222
+ status: 10,
223
+ reject_reason: rejectReason
208
224
  },
209
225
  deliveryFailedByDriver: {
210
- comment: _comment,
211
- status: 12
226
+ comment: comments,
227
+ status: 12,
228
+ reject_reason: rejectReason
212
229
  },
213
230
  orderNotReady: {
214
- comment: _comment,
215
- status: 14
231
+ comment: comments,
232
+ status: 14,
233
+ reject_reason: rejectReason
216
234
  },
217
235
  forcePickUp: {
218
- reasons: _comment,
219
- status: 9
236
+ reasons: comments,
237
+ status: 9,
238
+ reject_reason: rejectReason
220
239
  },
221
240
  forceDelivery: {
222
- reasons: _comment,
223
- status: 11
241
+ reasons: comments,
242
+ status: 11,
243
+ reject_reason: rejectReason
224
244
  }
225
245
  };
226
246
 
@@ -413,25 +433,45 @@ export const AcceptOrRejectOrder = (props: AcceptOrRejectOrderParams) => {
413
433
  onBlur={() => actions && action === 'accept' && timerRef?.current?.focus?.()}
414
434
  />
415
435
 
416
- {orderCommentsList && (
436
+ {orderCommentsList && isDriverApp && (
417
437
  <CommentsButtonGroup>
418
- {orderCommentsList?.list?.map((comment: any) => (
419
- <OButton
420
- key={comment.key}
421
- text={comment.content}
422
- bgColor={isSelectedComment(comment.key) ? theme.colors.primary : theme.colors.tabBar}
423
- borderColor={isSelectedComment(comment.key) ? theme.colors.primary : theme.colors.tabBar}
424
- textStyle={{
425
- color: isSelectedComment(comment.key) ? theme.colors.white : theme.colors.darkText,
426
- fontSize: 12,
427
- paddingRight: isSelectedComment(comment.key) ? 15 : 0
428
- }}
429
- style={{ height: 35, paddingLeft: 5, paddingRight: 5, marginHorizontal: 3, marginVertical: 10 }}
430
- imgRightSrc={isSelectedComment(comment.key) ? theme.images.general.close : null}
431
- imgRightStyle={{ tintColor: theme.colors.white, right: 5, margin: 5 }}
432
- onClick={() => handleChangeComments(comment) }
433
- />
434
- ))}
438
+ <SelectDropdown
439
+ defaultButtonText={t('REJECT_REASONS_OPTIONS', 'Reject reasons')}
440
+ data={orderCommentsList?.list}
441
+ onSelect={(selectedItem) => {
442
+ setRejectReason(selectedItem?.value)
443
+ }}
444
+ buttonTextAfterSelection={(selectedItem) => selectedItem.content}
445
+ rowTextForSelection={(item) => item.key}
446
+ buttonStyle={styles.selectOption}
447
+ buttonTextStyle={styles.buttonTextStyle}
448
+ renderDropdownIcon={isOpened => {
449
+ return <FeatherIcon name={isOpened ? 'chevron-up' : 'chevron-down'} color={'#444'} size={18} />;
450
+ }}
451
+ dropdownStyle={styles.dropdownStyle}
452
+ dropdownOverlayColor='transparent'
453
+ rowStyle={styles.rowStyle}
454
+ renderCustomizedRowChild={(item) => {
455
+ return (
456
+ <View
457
+ style={{
458
+ flexDirection: 'row',
459
+ alignItems: 'center'
460
+ }}
461
+ >
462
+ <View>
463
+ <OText
464
+ size={14}
465
+ color={'#748194'}
466
+ style={{ textTransform: 'capitalize' }}
467
+ >
468
+ {item?.content}
469
+ </OText>
470
+ </View>
471
+ </View>
472
+ );
473
+ }}
474
+ />
435
475
  </CommentsButtonGroup>
436
476
  )}
437
477
 
@@ -39,4 +39,5 @@ export const Comments = styled.View`
39
39
  export const CommentsButtonGroup = styled.View`
40
40
  flex-direction: row;
41
41
  flex-wrap: wrap;
42
+ margin-top: 15px;
42
43
  `
@@ -44,6 +44,35 @@ import { USER_TYPE } from '../../config/constants';
44
44
 
45
45
  import SignatureScreen from 'react-native-signature-canvas';
46
46
 
47
+ const ORDER_STATUS: any = {
48
+ 0: 'ORDER_STATUS_PENDING',
49
+ 1: 'ORDERS_COMPLETED',
50
+ 2: 'ORDER_REJECTED',
51
+ 3: 'ORDER_STATUS_IN_BUSINESS',
52
+ 4: 'ORDER_READY',
53
+ 5: 'ORDER_REJECTED_RESTAURANT',
54
+ 6: 'ORDER_STATUS_CANCELLEDBYDRIVER',
55
+ 7: 'ORDER_STATUS_ACCEPTEDBYRESTAURANT',
56
+ 8: 'ORDER_CONFIRMED_ACCEPTED_BY_DRIVER',
57
+ 9: 'ORDER_PICKUP_COMPLETED_BY_DRIVER',
58
+ 10: 'ORDER_PICKUP_FAILED_BY_DRIVER',
59
+ 11: 'ORDER_DELIVERY_COMPLETED_BY_DRIVER',
60
+ 12: 'ORDER_DELIVERY_FAILED_BY_DRIVER',
61
+ 13: 'PREORDER',
62
+ 14: 'ORDER_NOT_READY',
63
+ 15: 'ORDER_PICKEDUP_COMPLETED_BY_CUSTOMER',
64
+ 16: 'ORDER_STATUS_CANCELLED_BY_CUSTOMER',
65
+ 17: 'ORDER_NOT_PICKEDUP_BY_CUSTOMER',
66
+ 18: 'ORDER_DRIVER_ALMOST_ARRIVED_BUSINESS',
67
+ 19: 'ORDER_DRIVER_ALMOST_ARRIVED_CUSTOMER',
68
+ 20: 'ORDER_CUSTOMER_ALMOST_ARRIVED_BUSINESS',
69
+ 21: 'ORDER_CUSTOMER_ARRIVED_BUSINESS',
70
+ 22: 'ORDER_LOOKING_FOR_DRIVER',
71
+ 23: 'ORDER_DRIVER_ON_WAY'
72
+ }
73
+
74
+ const filterSpecialStatus = ['prepared_in', 'delivered_in', 'delivery_datetime']
75
+
47
76
  const ChatUI = (props: MessagesParams) => {
48
77
  const {
49
78
  type,
@@ -317,71 +346,6 @@ const ChatUI = (props: MessagesParams) => {
317
346
  }
318
347
  };
319
348
 
320
- const getStatus = (status: number, attribute: any) => {
321
- const hour = Math.trunc(status / 60);
322
- const min = Math.round((status / 60 - hour) * 60);
323
-
324
- if (attribute === 'status') {
325
- switch (status) {
326
- case 0:
327
- return 'ORDER_STATUS_PENDING';
328
- case 1:
329
- return 'ORDERS_COMPLETED';
330
- case 2:
331
- return 'ORDER_REJECTED';
332
- case 3:
333
- return 'ORDER_STATUS_IN_BUSINESS';
334
- case 4:
335
- return 'ORDER_READY';
336
- case 5:
337
- return 'ORDER_REJECTED_RESTAURANT';
338
- case 6:
339
- return 'ORDER_STATUS_CANCELLEDBYDRIVER';
340
- case 7:
341
- return 'ORDER_STATUS_ACCEPTEDBYRESTAURANT';
342
- case 8:
343
- return 'ORDER_CONFIRMED_ACCEPTED_BY_DRIVER';
344
- case 9:
345
- return 'ORDER_PICKUP_COMPLETED_BY_DRIVER';
346
- case 10:
347
- return 'ORDER_PICKUP_FAILED_BY_DRIVER';
348
- case 11:
349
- return 'ORDER_DELIVERY_COMPLETED_BY_DRIVER';
350
- case 12:
351
- return 'ORDER_DELIVERY_FAILED_BY_DRIVER';
352
- case 13:
353
- return 'PREORDER';
354
- case 14:
355
- return 'ORDER_NOT_READY';
356
- case 15:
357
- return 'ORDER_PICKEDUP_COMPLETED_BY_CUSTOMER';
358
- case 16:
359
- return 'ORDER_STATUS_CANCELLED_BY_CUSTOMER';
360
- case 17:
361
- return 'ORDER_NOT_PICKEDUP_BY_CUSTOMER';
362
- case 18:
363
- return 'ORDER_DRIVER_ALMOST_ARRIVED_BUSINESS';
364
- case 19:
365
- return 'ORDER_DRIVER_ALMOST_ARRIVED_CUSTOMER';
366
- case 20:
367
- return 'ORDER_CUSTOMER_ALMOST_ARRIVED_BUSINESS';
368
- case 21:
369
- return 'ORDER_CUSTOMER_ARRIVED_BUSINESS';
370
- case 22:
371
- return 'ORDER_LOOKING_FOR_DRIVER'
372
- case 23:
373
- return 'ORDER_DRIVER_ON_WAY'
374
- default:
375
- return ``;
376
- }
377
- }
378
-
379
- if (attribute === 'prepared_in' || attribute === 'delivered_in') {
380
- return `${hour < 10 ? '0' + hour : hour}:${min < 10 ? '0' + min : min} ${status > 60 ? 'hours' : 'minutes'
381
- }`;
382
- }
383
- };
384
-
385
349
  const onSubmit = (values: any) => {
386
350
  handleSend && handleSend();
387
351
  setImage && setImage(null);
@@ -403,28 +367,16 @@ const ChatUI = (props: MessagesParams) => {
403
367
  numberOfLines={3}
404
368
  style={{ ...styles.firstMessageText, textAlign: 'center' }}>
405
369
  {message.change?.attribute !== 'driver_id'
406
- ? `${t('ORDER', 'Order')} ${t(
407
- message.change.attribute.toUpperCase(),
408
- message.change.attribute,
409
- )} ${t('CHANGED_FROM', 'Changed from')} ${message.change.old !== null
410
- ? t(
411
- getStatus(
412
- parseInt(message.change.old, 10),
413
- message.change?.attribute,
414
- ),
415
- )
416
- : '0'
417
- } ${t('TO', 'to')} ${t(
418
- getStatus(
419
- parseInt(message.change.new, 10),
420
- message.change?.attribute,
421
- ),
422
- )}`
370
+ ?
371
+ `${t('ORDER', 'Order')} ${t(message.change.attribute.toUpperCase(), message.change.attribute.replace('_', ' '))} ${t('CHANGED_FROM', 'Changed from')} ${filterSpecialStatus.includes(message.change.attribute) ?
372
+ `${message.change.old === null ? '0' : message.change.old} ${t('TO', 'to')} ${message.change.new} ${t('MINUTES', 'Minutes')}` :
373
+ `${message.change.old !== null && t(ORDER_STATUS[parseInt(message.change.old, 10)])} ${t('TO', 'to')} ${t(ORDER_STATUS[parseInt(message.change.new, 10)])}`
374
+ }`
423
375
  : message.change.new
424
- ? `${message.driver?.name} ${message.driver?.lastname !== null ? message.driver.lastname : ''
425
- } ${t('WAS_ASSIGNED_AS_DRIVER', 'Was assigned as driver')} ${message.comment ? message.comment.length : ''
426
- }`
427
- : `${t('DRIVER_UNASSIGNED', 'Driver unassigned')}`}
376
+ ?
377
+ `${message.driver?.name} ${message.driver?.lastname !== null ? message.driver.lastname : ''} ${t('WAS_ASSIGNED_AS_DRIVER', 'Was assigned as driver')} ${message.comment ? message.comment.length : ''}`
378
+ :
379
+ `${t('DRIVER_UNASSIGNED', 'Driver unassigned')}`}
428
380
  </OText>
429
381
  <OText size={10} color={'#aaa'} style={{ alignSelf: 'flex-start' }}>
430
382
  {parseTime(message?.created_at, { outputFormat: 'hh:mma' })}
@@ -8,6 +8,8 @@ import {
8
8
  ScrollView,
9
9
  } from 'react-native';
10
10
  import { useForm, Controller } from 'react-hook-form';
11
+ import Recaptcha from 'react-native-recaptcha-that-works'
12
+ import { TouchableOpacity } from 'react-native-gesture-handler';
11
13
  import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
12
14
  import {
13
15
  ToastType,
@@ -25,6 +27,7 @@ import {
25
27
  TabsContainer,
26
28
  OrSeparator,
27
29
  LineSeparator,
30
+ RecaptchaButton
28
31
  } from './styles';
29
32
  import { _setStoreData } from '../../providers/StoreUtil'
30
33
  import { OText, OButton, OInput, OIconButton, OModal } from '../shared';
@@ -47,7 +50,10 @@ const LoginFormUI = (props: LoginParams) => {
47
50
  handleCheckPhoneCode,
48
51
  setCheckPhoneCodeState,
49
52
  allowedLevels,
50
- useRootPoint
53
+ useRootPoint,
54
+ notificationState,
55
+ handleReCaptcha,
56
+ enableReCaptcha
51
57
  } = props;
52
58
 
53
59
  const [ordering, { setOrdering }] = useApi();
@@ -61,6 +67,7 @@ const LoginFormUI = (props: LoginParams) => {
61
67
  const inputRef = useRef<any>(null);
62
68
  const inputMailRef = useRef<any>(null);
63
69
 
70
+ const [projectName, setProjectName] = useState('');
64
71
  const [passwordSee, setPasswordSee] = useState(false);
65
72
  const [isLoadingVerifyModal, setIsLoadingVerifyModal] = useState(false);
66
73
  const [isModalVisible, setIsModalVisible] = useState(false);
@@ -84,6 +91,49 @@ const LoginFormUI = (props: LoginParams) => {
84
91
  const [submitted, setSubmitted] = useState(false);
85
92
  const [formValues, setFormValues] = useState(null);
86
93
 
94
+ const [recaptchaConfig, setRecaptchaConfig] = useState<any>({})
95
+ const [recaptchaVerified, setRecaptchaVerified] = useState(false)
96
+
97
+ const recaptchaRef = useRef<any>({});
98
+
99
+ const handleOpenRecaptcha = () => {
100
+ setRecaptchaVerified(false)
101
+ if (!recaptchaConfig?.siteKey) {
102
+ showToast(ToastType.Error, t('NO_RECAPTCHA_SITE_KEY', 'The config doesn\'t have recaptcha site key'));
103
+ return
104
+ }
105
+ if (!recaptchaConfig?.baseUrl) {
106
+ showToast(ToastType.Error, t('NO_RECAPTCHA_BASE_URL', 'The config doesn\'t have recaptcha base url'));
107
+ return
108
+ }
109
+ recaptchaRef.current.open()
110
+ }
111
+
112
+ const onRecaptchaVerify = (token: any) => {
113
+ setRecaptchaVerified(true)
114
+ handleReCaptcha(token)
115
+ }
116
+
117
+ useEffect(() => {
118
+ if (configs && Object.keys(configs).length > 0 && enableReCaptcha) {
119
+ setRecaptchaConfig({
120
+ siteKey: configs?.security_recaptcha_site_key?.value || null,
121
+ baseUrl: configs?.security_recaptcha_base_url?.value || null
122
+ })
123
+ }
124
+ }, [configs, enableReCaptcha])
125
+
126
+ useEffect(() => {
127
+ const projectInputInterval = setInterval(() => {
128
+ if (projectName && useRootPoint) {
129
+ setOrdering({
130
+ ...ordering,
131
+ project: projectName
132
+ })
133
+ }
134
+ }, 1500)
135
+ return () => clearInterval(projectInputInterval);
136
+ }, [projectName])
87
137
  const getTraduction = (key: string) => {
88
138
  const keyList: any = {
89
139
  // Add the key and traduction that you need below
@@ -465,6 +515,7 @@ const LoginFormUI = (props: LoginParams) => {
465
515
  icon={theme.images.general.project}
466
516
  iconColor={theme.colors.arrowColor}
467
517
  onChange={(e: any) => {
518
+ setProjectName(e?.target?.value)
468
519
  onChange(e?.target?.value);
469
520
  setSubmitted(false);
470
521
  }}
@@ -590,7 +641,7 @@ const LoginFormUI = (props: LoginParams) => {
590
641
 
591
642
  {onNavigationRedirect && (
592
643
  <Pressable
593
- style={{ marginRight: 'auto', marginBottom: 35 }}
644
+ style={{ marginRight: 'auto', marginBottom: 20 }}
594
645
  onPress={() => onNavigationRedirect('Forgot')}>
595
646
  <OText style={styles.textForgot}>
596
647
  {t('FORGOT_YOUR_PASSWORD', 'Forgot your password?')}
@@ -598,6 +649,39 @@ const LoginFormUI = (props: LoginParams) => {
598
649
  </Pressable>
599
650
  )}
600
651
 
652
+ {enableReCaptcha && (
653
+ <>
654
+ <TouchableOpacity
655
+ style={{ marginBottom: 15 }}
656
+ onPress={handleOpenRecaptcha}
657
+ >
658
+ <RecaptchaButton>
659
+ {recaptchaVerified ? (
660
+ <MaterialCommunityIcons
661
+ name="checkbox-marked"
662
+ size={26}
663
+ color={theme.colors.primary}
664
+ />
665
+ ) : (
666
+ <MaterialCommunityIcons
667
+ name="checkbox-blank-outline"
668
+ size={26}
669
+ color={theme.colors.mediumGray}
670
+ />
671
+ )}
672
+ <OText size={14} mLeft={8}>{t('VERIFY_ReCAPTCHA', 'Verify reCAPTCHA')}</OText>
673
+ </RecaptchaButton>
674
+ </TouchableOpacity>
675
+ <Recaptcha
676
+ ref={recaptchaRef}
677
+ siteKey={recaptchaConfig?.siteKey}
678
+ baseUrl={recaptchaConfig?.baseUrl}
679
+ onVerify={onRecaptchaVerify}
680
+ onExpire={() => setRecaptchaVerified(false)}
681
+ />
682
+ </>
683
+ )}
684
+
601
685
  <OButton
602
686
  onClick={handleLogin}
603
687
  text={t('LOGIN', 'Login')}
@@ -657,6 +741,7 @@ const LoginFormUI = (props: LoginParams) => {
657
741
  export const LoginForm = (props: any) => {
658
742
  const loginProps = {
659
743
  ...props,
744
+ isRecaptchaEnable: true,
660
745
  UIComponent: LoginFormUI,
661
746
  };
662
747
 
@@ -46,3 +46,9 @@ export const LineSeparator = styled.View`
46
46
  height: 1px;
47
47
  background-color: ${(props: any) => props.theme.colors.disabled};
48
48
  `;
49
+
50
+ export const RecaptchaButton = styled.View`
51
+ flex-direction: row;
52
+ align-items: center;
53
+ margin-bottom: 10px;
54
+ `
@@ -14,7 +14,8 @@ Sound.setCategory('Playback')
14
14
 
15
15
  const windowWidth = Dimensions.get('screen').width
16
16
 
17
- const NewOrderNotificationUI = () => {
17
+ const NewOrderNotificationUI = (props: any) => {
18
+ const { isBusinessApp } = props
18
19
  const [events] = useEvent()
19
20
  const theme = useTheme()
20
21
  const [, t] = useLanguage()
@@ -76,13 +77,14 @@ const NewOrderNotificationUI = () => {
76
77
  headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }
77
78
  })
78
79
  const assignedTimeDiff = moment.utc(value?.driver?.last_order_assigned_at).local().fromNow()
79
- if (assignedTimeDiff === 'a few seconds ago') {
80
+ if (assignedTimeDiff === 'a few seconds ago' && !isBusinessApp) {
80
81
  handlePlayNotificationSound()
81
82
  clearInterval(soundTimeout)
82
83
  setCurrentEvent({ evt: 2, orderId: value?.id })
83
84
  }
84
85
  return
85
86
  }
87
+ if (evtType === 3) return
86
88
  handlePlayNotificationSound()
87
89
  clearInterval(soundTimeout)
88
90
  setCurrentEvent({
@@ -1,6 +1,6 @@
1
1
  //React & React Native
2
2
  import React, { useState, useEffect } from 'react';
3
- import { StyleSheet, View, Platform, ScrollView } from 'react-native';
3
+ import { StyleSheet, View } from 'react-native';
4
4
 
5
5
  // Thirds
6
6
  import { Placeholder, PlaceholderLine, Fade } from 'rn-placeholder';
@@ -29,7 +29,6 @@ import { NotFoundSource } from '../NotFoundSource';
29
29
  import { getOrderStatus } from '../../utils';
30
30
  import { OrderHeaderComponent } from './OrderHeaderComponent';
31
31
  import { OrderContentComponent } from './OrderContentComponent';
32
-
33
32
  //Styles
34
33
  import { OrderDetailsContainer, Pickup } from './styles';
35
34
 
@@ -52,7 +51,8 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
52
51
  appTitle,
53
52
  handleClickLogisticOrder,
54
53
  forceUpdate,
55
- getPermissions
54
+ getPermissions,
55
+ isGrantedPermissions
56
56
  } = props;
57
57
  const [, { showToast }] = useToast();
58
58
  const { order } = props.order
@@ -102,6 +102,10 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
102
102
  };
103
103
 
104
104
  const handleOpenMapView = async () => {
105
+ if (!isGrantedPermissions) {
106
+ navigation.navigate('RequestPermissions')
107
+ return
108
+ }
105
109
  const _permissions = await getPermissions()
106
110
 
107
111
  const isBlocked = _permissions.some((_permission: string) => permissions?.locationStatus?.[_permission] === 'blocked')
@@ -128,6 +132,10 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
128
132
  };
129
133
 
130
134
  const handleViewActionOrder = (action: string) => {
135
+ if (!isGrantedPermissions) {
136
+ navigation.navigate('RequestPermissions')
137
+ return
138
+ }
131
139
  if (openModalForMapView) {
132
140
  setOpenModalForMapView(false);
133
141
  }
@@ -617,7 +617,7 @@ const OrdersListManagerUI = (props: OrdersOptionParams) => {
617
617
  </RightSide>
618
618
  </Sides>
619
619
 
620
- <NewOrderNotification />
620
+ <NewOrderNotification isBusinessApp={isBusinessApp} />
621
621
  {(openSearchModal || openSLASettingModal) && (
622
622
  <OModal open={openSearchModal || openSLASettingModal} entireModal customClose>
623
623
  <ModalContainer
@@ -726,7 +726,7 @@ const OrdersOptionUI = (props: OrdersOptionParams) => {
726
726
  </View>
727
727
  {/* </GestureRecognizer> */}
728
728
 
729
- <NewOrderNotification />
729
+ <NewOrderNotification isBusinessApp={isBusinessApp} />
730
730
  {(openSearchModal || openSLASettingModal) && (
731
731
  <OModal open={openSearchModal || openSLASettingModal} entireModal customClose>
732
732
  <ModalContainer
@@ -20,6 +20,9 @@ export interface LoginParams {
20
20
  passwordInputIcon?: any;
21
21
  allowedLevels?: any;
22
22
  useRootPoint?: any;
23
+ notificationState?: any;
24
+ handleReCaptcha?: any;
25
+ enableReCaptcha?: any;
23
26
  }
24
27
  export interface ProfileParams {
25
28
  navigation?: any;
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { Platform, Text, StyleSheet } from 'react-native';
3
- import { useApi, useSession, useLanguage, useConfig, useToast, ToastType } from 'ordering-components/native';
3
+ import { useApi, useSession, useLanguage, useConfig } from 'ordering-components/native';
4
4
  import { appleAuthAndroid, appleAuth } from '@invertase/react-native-apple-authentication';
5
5
  import uuid from 'react-native-uuid';
6
6
  import Icon from 'react-native-vector-icons/FontAwesome5';
@@ -19,14 +19,13 @@ export const AppleLogin = (props: any) => {
19
19
  const [{ auth }] = useSession();
20
20
  const [, t] = useLanguage();
21
21
  const [{ configs }] = useConfig();
22
- const [, {showToast}] = useToast()
23
22
  const [credentialStateForUser, updateCredentialStateForUser] = useState<any>(-1);
24
23
 
25
24
  let user : any= null
26
25
 
27
26
  const buttonText = auth
28
27
  ? t('CONTINUE_WITH_APPLE', 'Logout with Apple')
29
- : t('CONTINUE_WITH_FACEBOOK', 'Continue with Apple');
28
+ : t('CONTINUE_WITH_APPLE', 'Continue with Apple');
30
29
 
31
30
  const performAppleLogin = async (code: string) => {
32
31
  try {
@@ -88,7 +87,6 @@ export const AppleLogin = (props: any) => {
88
87
  );
89
88
 
90
89
  if (identityToken && authorizationCode) {
91
- showToast(ToastType.Success, `Apple Authentication Completed, ${email}`)
92
90
  performAppleLogin(authorizationCode)
93
91
  } else {
94
92
  handleErrors && handleErrors('UNABLE_LOGIN_TOKEN', 'Unable to login, no token found')
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useState } from 'react'
2
2
  import { useLanguage, BusinessSearchList, useOrder, useUtils } from 'ordering-components/native'
3
- import { ScrollView, StyleSheet, TouchableOpacity, Platform, View } from 'react-native'
3
+ import { ScrollView, StyleSheet, TouchableOpacity, Platform, View, Dimensions } from 'react-native'
4
4
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
5
5
  import { useTheme } from 'styled-components/native'
6
6
  import { OButton, OIcon, OModal, OText } from '../shared'
@@ -22,15 +22,17 @@ import {
22
22
  ProgressContentWrapper,
23
23
  ProgressBar,
24
24
  TagsContainer,
25
- SortContainer
25
+ SortContainer,
26
+ BrandContainer,
27
+ BrandItem
26
28
  } from './styles'
27
29
  import FastImage from 'react-native-fast-image'
28
30
  import { convertHoursToMinutes } from '../../utils'
29
31
  import { Fade, Placeholder, PlaceholderLine } from 'rn-placeholder'
30
32
  import { BusinessSearchParams } from '../../types'
31
33
 
32
- export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
33
34
 
35
+ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
34
36
  const {
35
37
  navigation,
36
38
  businessesSearchList,
@@ -42,9 +44,11 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
42
44
  handleChangeFilters,
43
45
  filters,
44
46
  businessTypes,
45
- setFilters
47
+ setFilters,
48
+ brandList
46
49
  } = props
47
-
50
+
51
+ const screenHeight = Dimensions.get('window').height;
48
52
  const theme = useTheme()
49
53
  const [orderState] = useOrder()
50
54
  const { top } = useSafeAreaInsets();
@@ -68,6 +72,11 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
68
72
  paddingHorizontal: 40,
69
73
  width: '100%'
70
74
  },
75
+ filterContainer: {
76
+ maxHeight: screenHeight - 150,
77
+ paddingHorizontal: 40,
78
+ width: '100%'
79
+ },
71
80
  searchInput: {
72
81
  fontSize: 10,
73
82
  },
@@ -107,7 +116,7 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
107
116
  }
108
117
 
109
118
  const handleCloseFilters = () => {
110
- setFilters({ business_types: [], orderBy: 'default' })
119
+ setFilters({ business_types: [], orderBy: 'default', franchise_ids: [] })
111
120
  setOpenFilters(false)
112
121
  }
113
122
 
@@ -126,6 +135,13 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
126
135
  }
127
136
  }
128
137
 
138
+ const handleChangeBrandFilter = (brandId: number) => {
139
+ let franchiseIds = [...filters?.franchise_ids]
140
+ if (filters?.franchise_ids?.includes(brandId)) franchiseIds = filters?.franchise_ids?.filter((item: any) => item !== brandId)
141
+ else franchiseIds.push(brandId)
142
+ handleChangeFilters && handleChangeFilters('franchise_ids', franchiseIds)
143
+ }
144
+
129
145
  const handleApplyFilters = () => {
130
146
  handleSearchbusinessAndProducts(true)
131
147
  setOpenFilters(false)
@@ -388,7 +404,7 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
388
404
  onCancel={() => handleCloseFilters()}
389
405
  onClose={() => handleCloseFilters()}
390
406
  >
391
- <ScrollView style={styles.container}>
407
+ <ScrollView style={styles.filterContainer}>
392
408
  <OText
393
409
  size={20}
394
410
  mBottom={15}
@@ -415,6 +431,48 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
415
431
  </TouchableOpacity>
416
432
  ))}
417
433
  </SortContainer>
434
+ <BrandContainer>
435
+ <OText
436
+ size={16}
437
+ weight='bold'
438
+ lineHeight={24}
439
+ style={{ marginBottom: 10 }}
440
+ >
441
+ {t('BRANDS', 'Brands')}
442
+ </OText>
443
+ {!brandList?.loading && !brandList?.error && brandList?.brands?.length > 0 && (
444
+ <ScrollView
445
+ style={{ maxHeight: 300, marginBottom: 10 }}
446
+ showsVerticalScrollIndicator={true}
447
+ nestedScrollEnabled={true}
448
+ >
449
+ {brandList?.brands.map((brand: any, i: number) => brand?.enabled && (
450
+ <BrandItem
451
+ key={i}
452
+ onPress={() => handleChangeBrandFilter(brand?.id)}
453
+ >
454
+ <OText
455
+ size={14}
456
+ weight={'400'}
457
+ lineHeight={24}
458
+ >
459
+ {brand?.name}
460
+ </OText>
461
+ {filters?.franchise_ids?.includes(brand?.id) && (
462
+ <AntDesignIcon
463
+ name='check'
464
+ color={theme.colors.success500}
465
+ size={16}
466
+ />
467
+ )}
468
+ </BrandItem>
469
+ ))}
470
+ </ScrollView>
471
+ )}
472
+ {!brandList?.loading && ((brandList?.brands?.filter((brand: any) => brand?.enabled))?.length === 0) && (
473
+ <OText size={14} weight='400'>{t('NO_RESULTS_FOUND', 'Sorry, no results found')}</OText>
474
+ )}
475
+ </BrandContainer>
418
476
  {orderState?.options?.type === 1 && (
419
477
  <MaxSectionItem
420
478
  title={t('MAX_DELIVERY_FEE', 'Max delivery fee')}
@@ -68,9 +68,18 @@ export const ProgressContentWrapper = styled.View`
68
68
  `
69
69
 
70
70
  export const TagsContainer = styled.View`
71
-
71
+ padding-bottom: 10px;
72
72
  `
73
73
 
74
74
  export const SortContainer = styled.View`
75
75
  margin-bottom: 10px;
76
76
  `
77
+
78
+ export const BrandContainer = styled.View``
79
+
80
+ export const BrandItem = styled.TouchableOpacity`
81
+ flex-direction: row;
82
+ justify-content: space-between;
83
+ margin-bottom: 4px;
84
+ align-items: center;
85
+ `
@@ -438,7 +438,7 @@ const CartUI = (props: any) => {
438
438
  </BusinessItemAccordion>
439
439
 
440
440
  <OModal
441
- open={openChangeStore && props.isFranchiseApp}
441
+ open={openChangeStore}
442
442
  entireModal
443
443
  customClose
444
444
  onClose={() => setOpenChangeStore(false)}
@@ -135,7 +135,11 @@ const CheckoutUI = (props: any) => {
135
135
  const [webviewPaymethod, setWebviewPaymethod] = useState<any>(null)
136
136
 
137
137
  const placeSpotTypes = [3, 4]
138
- const isWalletEnabled = configs?.wallet_enabled?.value === '1' && (configs?.wallet_cash_enabled?.value === '1' || configs?.wallet_credit_point_enabled?.value === '1')
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)
142
+
139
143
  const isPreOrder = configs?.preorder_status_enabled?.value === '1'
140
144
  const isDisabledButtonPlace = loading || !cart?.valid || (!paymethodSelected && cart?.balance > 0) || placing || errorCash ||
141
145
  cart?.subtotal < cart?.minimum || (placeSpotTypes.includes(options?.type) && !cart?.place) ||
@@ -244,7 +248,7 @@ const CheckoutUI = (props: any) => {
244
248
 
245
249
  useEffect(() => {
246
250
  if (cart?.products?.length === 0) {
247
- onNavigationRedirect('Business', { store: cart?.business?.slug })
251
+ onNavigationRedirect('Business', { store: cart?.business?.slug, header: null, logo: null })
248
252
  }
249
253
  }, [cart?.products])
250
254
 
@@ -66,7 +66,6 @@ export const Otp = (props: otpParams) => {
66
66
  <OTPInputView
67
67
  style={{ width: '100%', height: 150 }}
68
68
  pinCount={6}
69
- autoFocusOnLoad
70
69
  codeInputFieldStyle={loginStyle.underlineStyleBase}
71
70
  codeInputHighlightStyle={loginStyle.underlineStyleHighLighted}
72
71
  onCodeFilled={(code: string) => handleLoginOtp(code)}
@@ -393,7 +393,7 @@ const LoginFormUI = (props: LoginParams) => {
393
393
  : theme.colors.disabled
394
394
  }
395
395
  weight={isOtpEmail ? 'bold' : 'normal'}>
396
- {t('LOGIN_BY_OTP_EMAIL', 'Login by Otp Email')}
396
+ {t('BY_OTP_EMAIL', 'By Otp Email')}
397
397
  </OText>
398
398
  </OTab>
399
399
  </TabBtn>
@@ -415,7 +415,7 @@ const LoginFormUI = (props: LoginParams) => {
415
415
  : theme.colors.disabled
416
416
  }
417
417
  weight={isOtpCellphone ? 'bold' : 'normal'}>
418
- {t('LOGIN_BY_OTP_PHONE', 'Login by Otp Phone')}
418
+ {t('BY_OTP_PHONE', 'By Otp Phone')}
419
419
  </OText>
420
420
  </OTab>
421
421
  </TabBtn>
@@ -12,11 +12,9 @@ export const FormSide = styled.View`
12
12
  margin: auto;
13
13
  `;
14
14
 
15
- export const OTabs = styled.View`
15
+ export const OTabs = styled.ScrollView`
16
16
  flex-direction: row;
17
17
  width: 100%;
18
- flex-wrap: wrap;
19
- justify-content: flex-start;
20
18
  margin-bottom: -1px;
21
19
  `;
22
20
 
@@ -175,6 +175,7 @@ export const ProductOptionsUI = (props: any) => {
175
175
  const [optionLayout, setOptionLayout] = useState<any>({})
176
176
  const [headerRefHeight, setHeaderRefHeight] = useState(0)
177
177
  const [summaryRefHeight, setSummaryRefHeight] = useState(0)
178
+ const [isScrollAvailable, setIsScrollAvailable] = useState(null)
178
179
 
179
180
  const isError = (id: number) => {
180
181
  let bgColor = theme.colors.white;
@@ -278,6 +279,52 @@ export const ProductOptionsUI = (props: any) => {
278
279
  setOptionLayout(_optionLayout)
279
280
  }
280
281
 
282
+ const saveErrors =
283
+ orderState.loading ||
284
+ maxProductQuantity === 0 ||
285
+ Object.keys(errors).length > 0;
286
+
287
+ const ExtraOptions = ({ eID, options }: any) => (
288
+ <>
289
+ {options.map(({ id, name, respect_to, suboptions }: any) => (
290
+ <React.Fragment key={`cont_key_${id}`}>
291
+ {respect_to == null && suboptions?.length > 0 && (
292
+ <TouchableOpacity
293
+ key={`eopt_key_${id}`}
294
+ onPress={() => setSelectedOpt(id)}
295
+ style={[
296
+ styles.extraItem,
297
+ {
298
+ borderBottomColor:
299
+ selOpt == id ? theme.colors.textNormal : theme.colors.border,
300
+ },
301
+ ]}>
302
+ <OText
303
+ color={
304
+ selOpt == id ? theme.colors.textNormal : theme.colors.textSecondary
305
+ }
306
+ size={selOpt == id ? 14 : 12}
307
+ weight={selOpt == id ? '600' : 'normal'}>
308
+ {name}
309
+ </OText>
310
+ </TouchableOpacity>
311
+ )}
312
+ </React.Fragment>
313
+ ))}
314
+ </>
315
+ );
316
+
317
+ const handleGoBack = navigation?.canGoBack()
318
+ ? () => navigation.goBack()
319
+ : () => navigation.navigate('Business', { store: props.businessSlug })
320
+
321
+ useEffect(() => {
322
+ if (isScrollAvailable) {
323
+ setIsScrollAvailable(null)
324
+ scrollDown(isScrollAvailable)
325
+ }
326
+ }, [errors])
327
+
281
328
  useEffect(() => {
282
329
  const imageList: any = []
283
330
  const videoList: any = []
@@ -319,45 +366,6 @@ export const ProductOptionsUI = (props: any) => {
319
366
  }
320
367
  }, [product])
321
368
 
322
- const saveErrors =
323
- orderState.loading ||
324
- maxProductQuantity === 0 ||
325
- Object.keys(errors).length > 0;
326
-
327
- const ExtraOptions = ({ eID, options }: any) => (
328
- <>
329
- {options.map(({ id, name, respect_to, suboptions }: any) => (
330
- <React.Fragment key={`cont_key_${id}`}>
331
- {respect_to == null && suboptions?.length > 0 && (
332
- <TouchableOpacity
333
- key={`eopt_key_${id}`}
334
- onPress={() => setSelectedOpt(id)}
335
- style={[
336
- styles.extraItem,
337
- {
338
- borderBottomColor:
339
- selOpt == id ? theme.colors.textNormal : theme.colors.border,
340
- },
341
- ]}>
342
- <OText
343
- color={
344
- selOpt == id ? theme.colors.textNormal : theme.colors.textSecondary
345
- }
346
- size={selOpt == id ? 14 : 12}
347
- weight={selOpt == id ? '600' : 'normal'}>
348
- {name}
349
- </OText>
350
- </TouchableOpacity>
351
- )}
352
- </React.Fragment>
353
- ))}
354
- </>
355
- );
356
-
357
- const handleGoBack = navigation?.canGoBack()
358
- ? () => navigation.goBack()
359
- : () => navigation.navigate('Business', { store: props.businessSlug })
360
-
361
369
  return (
362
370
  <SafeAreaView style={{ flex: 1 }}>
363
371
  <TopHeader>
@@ -738,7 +746,7 @@ export const ProductOptionsUI = (props: any) => {
738
746
  isSoldOut ||
739
747
  maxProductQuantity <= 0
740
748
  }
741
- scrollDown={scrollDown}
749
+ setIsScrollAvailable={setIsScrollAvailable}
742
750
  error={errors[`id:${option.id}`]}
743
751
  />
744
752
  );
@@ -28,36 +28,40 @@ export const ProductOptionSubOptionUI = (props: any) => {
28
28
  toggleSelect,
29
29
  changePosition,
30
30
  disabled,
31
- error,
32
- scrollDown
31
+ setIsScrollAvailable
33
32
  } = props
34
33
 
34
+ const disableIncrement = option?.limit_suboptions_by_max ? balance === option?.max : state.quantity === suboption?.max || (!state.selected && balance === option?.max)
35
+ const price = option?.with_half_option && suboption?.half_price && state.position !== 'whole' ? suboption?.half_price : suboption?.price
36
+
35
37
  const theme = useTheme();
36
-
37
38
  const [, t] = useLanguage()
38
39
  const [{ parsePrice }] = useUtils()
39
40
  const [showMessage, setShowMessage] = useState(false)
41
+ const [isDirty, setIsDirty] = useState(false)
40
42
 
41
43
  const handleSuboptionClick = () => {
42
44
  toggleSelect()
43
-
44
- if (balance === option?.max - 1 && !state.selected) {
45
- scrollDown(option?.id)
46
- }
45
+ setIsDirty(true)
47
46
 
48
47
  if (balance === option?.max && option?.suboptions?.length > balance && !(option?.min === 1 && option?.max === 1)) {
49
48
  setShowMessage(true)
50
49
  }
51
50
  }
52
51
 
52
+ useEffect(() => {
53
+ if (balance === option?.max && state?.selected && isDirty) {
54
+ setIsDirty(false)
55
+ setIsScrollAvailable(option?.id)
56
+ }
57
+ }, [state?.selected])
58
+
53
59
  useEffect(() => {
54
60
  if (!(balance === option?.max && option?.suboptions?.length > balance && !(option?.min === 1 && option?.max === 1))) {
55
61
  setShowMessage(false)
56
62
  }
57
63
  }, [balance])
58
64
 
59
- const disableIncrement = option?.limit_suboptions_by_max ? balance === option?.max : state.quantity === suboption?.max || (!state.selected && balance === option?.max)
60
- const price = option?.with_half_option && suboption?.half_price && state.position !== 'whole' ? suboption?.half_price : suboption?.price
61
65
  return (
62
66
  <View>
63
67
  <Container onPress={() => handleSuboptionClick()}>
@@ -109,7 +109,7 @@ const ProfileListUI = (props: ProfileParams) => {
109
109
 
110
110
  const [confirm, setConfirm] = useState<any>({ open: false, content: null, handleOnAccept: null, id: null, title: null })
111
111
 
112
- const isWalletEnabled = configs?.wallet_enabled?.value === '1' && (configs?.wallet_cash_enabled?.value === '1' || configs?.wallet_credit_point_enabled?.value === '1')
112
+ const isWalletEnabled = configs?.cash_wallet?.value && configs?.wallet_enabled?.value === '1' && (configs?.wallet_cash_enabled?.value === '1' || configs?.wallet_credit_point_enabled?.value === '1')
113
113
  const IsPromotionsEnabled = configs?.advanced_offers_module?.value === '1' || configs?.advanced_offers_module?.value === true
114
114
  const onRedirect = (route: string, params?: any) => {
115
115
  navigation.navigate(route, params)
@@ -61,7 +61,7 @@ const WalletsUI = (props: any) => {
61
61
 
62
62
  const [tabSelected, setTabSelected] = useState(isWalletCashEnabled ? 'cash' : 'credit_point')
63
63
 
64
- const isWalletEnabled = configs?.wallet_enabled?.value === '1' && (isWalletCashEnabled || isWalletPointsEnabled)
64
+ const isWalletEnabled = configs?.cash_wallet?.value && configs?.wallet_enabled?.value === '1' && (isWalletCashEnabled || isWalletPointsEnabled)
65
65
 
66
66
  const currentWalletSelected = (walletList.wallets?.length > 0 && walletList.wallets?.find((w: any) => w.type === tabSelected)) ?? null
67
67
 
@@ -576,7 +576,8 @@ export interface BusinessSearchParams {
576
576
  filters: any,
577
577
  businessTypes: Array<number>,
578
578
  setFilters: (filters: any) => void,
579
- lazySearch?: boolean
579
+ lazySearch?: boolean,
580
+ brandList?: any;
580
581
  }
581
582
 
582
583
  export interface NoNetworkParams {