ordering-ui-react-native 0.15.89 → 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.
Files changed (31) hide show
  1. package/package.json +1 -1
  2. package/src/components/Checkout/index.tsx +23 -2
  3. package/src/utils/index.tsx +28 -28
  4. package/themes/business/src/components/AcceptOrRejectOrder/index.tsx +100 -60
  5. package/themes/business/src/components/AcceptOrRejectOrder/styles.tsx +1 -0
  6. package/themes/business/src/components/Chat/index.tsx +38 -86
  7. package/themes/business/src/components/LoginForm/index.tsx +89 -2
  8. package/themes/business/src/components/LoginForm/styles.tsx +6 -0
  9. package/themes/business/src/components/NewOrderNotification/index.tsx +26 -13
  10. package/themes/business/src/components/OrderDetails/Delivery.tsx +11 -3
  11. package/themes/business/src/components/OrderDetails/OrderContentComponent.tsx +1 -0
  12. package/themes/business/src/components/OrdersListManager/index.tsx +1 -1
  13. package/themes/business/src/components/OrdersOption/index.tsx +5 -2
  14. package/themes/business/src/types/index.tsx +3 -0
  15. package/themes/original/src/components/AppleLogin/index.tsx +2 -4
  16. package/themes/original/src/components/BusinessListingSearch/index.tsx +117 -7
  17. package/themes/original/src/components/BusinessListingSearch/styles.tsx +14 -1
  18. package/themes/original/src/components/BusinessesListing/index.tsx +3 -1
  19. package/themes/original/src/components/Cart/index.tsx +1 -1
  20. package/themes/original/src/components/Checkout/index.tsx +37 -29
  21. package/themes/original/src/components/Help/index.tsx +21 -4
  22. package/themes/original/src/components/LastOrders/index.tsx +12 -1
  23. package/themes/original/src/components/LoginForm/Otp/index.tsx +0 -1
  24. package/themes/original/src/components/LoginForm/index.tsx +42 -9
  25. package/themes/original/src/components/LoginForm/styles.tsx +1 -3
  26. package/themes/original/src/components/OrderDetails/index.tsx +18 -21
  27. package/themes/original/src/components/PaymentOptionWallet/index.tsx +1 -0
  28. package/themes/original/src/components/ProductForm/index.tsx +48 -40
  29. package/themes/original/src/components/ProductOptionSubOption/index.tsx +13 -9
  30. package/themes/original/src/components/Promotions/index.tsx +18 -2
  31. package/themes/original/src/types/index.tsx +3 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ordering-ui-react-native",
3
- "version": "0.15.89",
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
@@ -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({name: '', isFocued: false});
64
71
  const [passwordSee, setPasswordSee] = useState(false);
65
72
  const [isLoadingVerifyModal, setIsLoadingVerifyModal] = useState(false);
66
73
  const [isModalVisible, setIsModalVisible] = useState(false);
@@ -84,6 +91,50 @@ 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.name && useRootPoint && projectName.isFocued) {
129
+ setOrdering({
130
+ ...ordering,
131
+ project: projectName.name
132
+ })
133
+ }
134
+ }, 1500)
135
+ return () => clearInterval(projectInputInterval);
136
+ }, [projectName])
137
+
87
138
  const getTraduction = (key: string) => {
88
139
  const keyList: any = {
89
140
  // Add the key and traduction that you need below
@@ -465,6 +516,7 @@ const LoginFormUI = (props: LoginParams) => {
465
516
  icon={theme.images.general.project}
466
517
  iconColor={theme.colors.arrowColor}
467
518
  onChange={(e: any) => {
519
+ setProjectName({name: e?.target?.value, isFocued: true})
468
520
  onChange(e?.target?.value);
469
521
  setSubmitted(false);
470
522
  }}
@@ -493,6 +545,7 @@ const LoginFormUI = (props: LoginParams) => {
493
545
  icon={theme.images.logos.emailInputIcon}
494
546
  iconColor={theme.colors.arrowColor}
495
547
  onChange={(e: any) => {
548
+ setProjectName({...projectName, isFocued: false})
496
549
  handleChangeInputEmail(e, onChange);
497
550
  }}
498
551
  selectionColor={theme.colors.primary}
@@ -590,7 +643,7 @@ const LoginFormUI = (props: LoginParams) => {
590
643
 
591
644
  {onNavigationRedirect && (
592
645
  <Pressable
593
- style={{ marginRight: 'auto', marginBottom: 35 }}
646
+ style={{ marginRight: 'auto', marginBottom: 20 }}
594
647
  onPress={() => onNavigationRedirect('Forgot')}>
595
648
  <OText style={styles.textForgot}>
596
649
  {t('FORGOT_YOUR_PASSWORD', 'Forgot your password?')}
@@ -598,6 +651,39 @@ const LoginFormUI = (props: LoginParams) => {
598
651
  </Pressable>
599
652
  )}
600
653
 
654
+ {enableReCaptcha && (
655
+ <>
656
+ <TouchableOpacity
657
+ style={{ marginBottom: 15 }}
658
+ onPress={handleOpenRecaptcha}
659
+ >
660
+ <RecaptchaButton>
661
+ {recaptchaVerified ? (
662
+ <MaterialCommunityIcons
663
+ name="checkbox-marked"
664
+ size={26}
665
+ color={theme.colors.primary}
666
+ />
667
+ ) : (
668
+ <MaterialCommunityIcons
669
+ name="checkbox-blank-outline"
670
+ size={26}
671
+ color={theme.colors.mediumGray}
672
+ />
673
+ )}
674
+ <OText size={14} mLeft={8}>{t('VERIFY_ReCAPTCHA', 'Verify reCAPTCHA')}</OText>
675
+ </RecaptchaButton>
676
+ </TouchableOpacity>
677
+ <Recaptcha
678
+ ref={recaptchaRef}
679
+ siteKey={recaptchaConfig?.siteKey}
680
+ baseUrl={recaptchaConfig?.baseUrl}
681
+ onVerify={onRecaptchaVerify}
682
+ onExpire={() => setRecaptchaVerified(false)}
683
+ />
684
+ </>
685
+ )}
686
+
601
687
  <OButton
602
688
  onClick={handleLogin}
603
689
  text={t('LOGIN', 'Login')}
@@ -657,6 +743,7 @@ const LoginFormUI = (props: LoginParams) => {
657
743
  export const LoginForm = (props: any) => {
658
744
  const loginProps = {
659
745
  ...props,
746
+ isRecaptchaEnable: true,
660
747
  UIComponent: LoginFormUI,
661
748
  };
662
749