ordering-ui-react-native 0.15.80 → 0.15.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ordering-ui-react-native",
3
- "version": "0.15.80",
3
+ "version": "0.15.83",
4
4
  "description": "Reusable components made in react native",
5
5
  "main": "src/index.tsx",
6
6
  "author": "ordering.inc",
@@ -51,7 +51,8 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
51
51
  orderTitle,
52
52
  appTitle,
53
53
  handleClickLogisticOrder,
54
- forceUpdate
54
+ forceUpdate,
55
+ getPermissions
55
56
  } = props;
56
57
  const [, { showToast }] = useToast();
57
58
  const { order } = props.order
@@ -101,9 +102,14 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
101
102
  };
102
103
 
103
104
  const handleOpenMapView = async () => {
104
- if (permissions?.locationStatus === 'granted') {
105
+ const _permissions = await getPermissions()
106
+
107
+ const isBlocked = _permissions.some((_permission: string) => permissions?.locationStatus?.[_permission] === 'blocked')
108
+ const isGranted = _permissions.reduce((allPermissions: boolean, _permission: string) => allPermissions && permissions?.locationStatus?.[_permission] === 'granted', true)
109
+
110
+ if (isGranted) {
105
111
  setOpenModalForMapView(!openModalForMapView);
106
- } else if (permissions?.locationStatus === 'blocked') {
112
+ } else if (isBlocked) {
107
113
  // redirectToSettings();
108
114
  showToast(
109
115
  ToastType.Error,
@@ -114,8 +120,9 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
114
120
  );
115
121
  } else {
116
122
  const response = await askLocationPermission();
117
- if (response === 'granted') {
118
- setOpenModalForMapView(!openModalForMapView);
123
+ const isGranted = _permissions.reduce((allPermissions: boolean, _permission: string) => allPermissions && response?.locationStatus?.[_permission] === 'granted', true)
124
+ if (isGranted) {
125
+ setOpenModalForMapView(true)
119
126
  }
120
127
  }
121
128
  };
@@ -373,6 +373,7 @@ export interface OrderDetailsParams {
373
373
  handleClickLogisticOrder?: (status: number, orderId: number) => void;
374
374
  orderTitle?: any;
375
375
  forceUpdate?: number;
376
+ getPermissions?: any
376
377
  }
377
378
  export interface ProductItemAccordionParams {
378
379
  isCartPending?: boolean;
@@ -578,4 +579,4 @@ export interface ReviewCustomerParams {
578
579
 
579
580
  export interface NoNetworkParams {
580
581
  image?: any;
581
- }
582
+ }
@@ -162,7 +162,7 @@ const CartUI = (props: any) => {
162
162
  handleClearProducts={handleClearProducts}
163
163
  handleCartOpen={handleCartOpen}
164
164
  onNavigationRedirect={props.onNavigationRedirect}
165
- handleChangeStore={props.isFranchiseApp ? () => setOpenChangeStore(true) : null}
165
+ handleChangeStore={() => setOpenChangeStore(true)}
166
166
  handleClickCheckout={() => setOpenUpselling(true)}
167
167
  checkoutButtonDisabled={(openUpselling && !canOpenUpselling) || cart?.subtotal < cart?.minimum || !cart?.valid_address}
168
168
  >
@@ -597,21 +597,19 @@ const CheckoutUI = (props: any) => {
597
597
  </OText>
598
598
  </TouchableOpacity>
599
599
  </CartHeader>
600
- {props.isFranchiseApp && (
601
- <TouchableOpacity
602
- onPress={() => setOpenChangeStore(true)}
603
- style={{ alignSelf: 'flex-start' }}
600
+ <TouchableOpacity
601
+ onPress={() => setOpenChangeStore(true)}
602
+ style={{ alignSelf: 'flex-start' }}
603
+ >
604
+ <OText
605
+ size={12}
606
+ lineHeight={18}
607
+ color={theme.colors.textSecondary}
608
+ style={{ textDecorationLine: 'underline' }}
604
609
  >
605
- <OText
606
- size={12}
607
- lineHeight={18}
608
- color={theme.colors.textSecondary}
609
- style={{ textDecorationLine: 'underline' }}
610
- >
611
- {t('CHANGE_STORE', 'Change store')}
612
- </OText>
613
- </TouchableOpacity>
614
- )}
610
+ {t('CHANGE_STORE', 'Change store')}
611
+ </OText>
612
+ </TouchableOpacity>
615
613
  <OrderSummary
616
614
  cart={cart}
617
615
  isCartPending={cart?.status === 2}
@@ -664,7 +662,7 @@ const CheckoutUI = (props: any) => {
664
662
  </View>
665
663
  )}
666
664
  <OModal
667
- open={openChangeStore && props.isFranchiseApp}
665
+ open={openChangeStore}
668
666
  entireModal
669
667
  customClose
670
668
  onClose={() => setOpenChangeStore(false)}
@@ -873,7 +871,7 @@ export const Checkout = (props: any) => {
873
871
  ...props,
874
872
  UIComponent: CheckoutUI,
875
873
  cartState,
876
- [props.isFranchiseApp ? 'uuid' : 'businessId']: props.isFranchiseApp ? cartUuid : cartState.cart?.business_id
874
+ uuid: cartUuid
877
875
  }
878
876
 
879
877
  return (
@@ -1,16 +1,20 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useEffect, useState, useRef } from 'react';
2
2
  import { StyleSheet } from 'react-native';
3
3
  import { useForm, Controller } from 'react-hook-form';
4
+ import Recaptcha from 'react-native-recaptcha-that-works'
5
+ import { TouchableOpacity } from 'react-native-gesture-handler';
6
+ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
4
7
 
5
8
  import {
6
9
  ForgotPasswordForm as ForgotPasswordController,
7
10
  useLanguage,
8
11
  useToast,
9
12
  ToastType,
13
+ useConfig
10
14
  } from 'ordering-components/native';
11
15
  import { useTheme } from 'styled-components/native';
12
16
  import NavBar from '../NavBar';
13
- import { FormInput, FormSide } from '../LoginForm/styles'
17
+ import { FormInput, FormSide, RecaptchaButton } from '../LoginForm/styles'
14
18
  import { Container } from './styles'
15
19
 
16
20
  import { OButton, OInput, OText } from '../shared';
@@ -20,10 +24,15 @@ const ForgotPasswordUI = (props: any) => {
20
24
  navigation,
21
25
  formState,
22
26
  handleButtonForgotPasswordClick,
27
+ handleReCaptcha,
28
+ enableReCaptcha
23
29
  } = props;
24
30
  const [, t] = useLanguage();
25
31
  const [, { showToast }] = useToast();
32
+ const [{ configs }] = useConfig();
26
33
  const { control, handleSubmit, errors } = useForm();
34
+ const [recaptchaConfig, setRecaptchaConfig] = useState<any>({})
35
+ const [recaptchaVerified, setRecaptchaVerified] = useState(false)
27
36
 
28
37
  const theme = useTheme();
29
38
 
@@ -38,6 +47,7 @@ const ForgotPasswordUI = (props: any) => {
38
47
  });
39
48
 
40
49
  const [emailSent, setEmailSent] = useState(null);
50
+ const recaptchaRef = useRef<any>({});
41
51
 
42
52
  const onSubmit = (values: any) => {
43
53
  setEmailSent(values.email)
@@ -48,23 +58,52 @@ const ForgotPasswordUI = (props: any) => {
48
58
  onChange(value.toLowerCase().replace(/[&,()%";:ç?<>{}\\[\]\s]/g, ''))
49
59
  }
50
60
 
61
+ const handleOpenRecaptcha = () => {
62
+ setRecaptchaVerified(false)
63
+ if (!recaptchaConfig?.siteKey) {
64
+ showToast(ToastType.Error, t('NO_RECAPTCHA_SITE_KEY', 'The config doesn\'t have recaptcha site key'));
65
+ return
66
+ }
67
+ if (!recaptchaConfig?.baseUrl) {
68
+ showToast(ToastType.Error, t('NO_RECAPTCHA_BASE_URL', 'The config doesn\'t have recaptcha base url'));
69
+ return
70
+ }
71
+ recaptchaRef.current.open()
72
+ }
73
+
74
+ const onRecaptchaVerify = (token: any) => {
75
+ setRecaptchaVerified(true)
76
+ handleReCaptcha(token)
77
+ }
78
+
51
79
  useEffect(() => {
52
80
  if (!formState.loading && emailSent) {
53
81
  if (formState.result?.error) {
54
82
  setEmailSent(null)
55
83
  formState.result?.result && showToast(
56
84
  ToastType.Error,
57
- formState.result?.result[0]
85
+ typeof formState.result?.result === 'string'
86
+ ? formState.result?.result
87
+ : formState.result?.result[0]
58
88
  )
59
89
  return
60
90
  }
61
91
  showToast(
62
92
  ToastType.Success,
63
- `${t('SUCCESS_SEND_FORGOT_PASSWORD', 'Your link has been sent to the email')}: ${emailSent}`
93
+ t('IF_ACCOUNT_EXIST_EMAIL_SEND_PASSWORD', 'If an account exists with this email a password will be sent')
64
94
  )
65
95
  }
66
96
  }, [formState])
67
97
 
98
+ useEffect(() => {
99
+ if (configs && Object.keys(configs).length > 0 && enableReCaptcha) {
100
+ setRecaptchaConfig({
101
+ siteKey: configs?.security_recaptcha_site_key?.value || null,
102
+ baseUrl: configs?.security_recaptcha_base_url?.value || null
103
+ })
104
+ }
105
+ }, [configs, enableReCaptcha])
106
+
68
107
  return (
69
108
  <Container>
70
109
  <NavBar
@@ -126,6 +165,37 @@ const ForgotPasswordUI = (props: any) => {
126
165
  }}
127
166
  defaultValue=""
128
167
  />
168
+ {enableReCaptcha && (
169
+ <>
170
+ <TouchableOpacity
171
+ onPress={handleOpenRecaptcha}
172
+ >
173
+ <RecaptchaButton>
174
+ {recaptchaVerified ? (
175
+ <MaterialCommunityIcons
176
+ name="checkbox-marked"
177
+ size={26}
178
+ color={theme.colors.primary}
179
+ />
180
+ ) : (
181
+ <MaterialCommunityIcons
182
+ name="checkbox-blank-outline"
183
+ size={26}
184
+ color={theme.colors.mediumGray}
185
+ />
186
+ )}
187
+ <OText size={14} mLeft={8}>{t('VERIFY_ReCAPTCHA', 'Verify reCAPTCHA')}</OText>
188
+ </RecaptchaButton>
189
+ </TouchableOpacity>
190
+ <Recaptcha
191
+ ref={recaptchaRef}
192
+ siteKey={recaptchaConfig?.siteKey}
193
+ baseUrl={recaptchaConfig?.baseUrl}
194
+ onVerify={onRecaptchaVerify}
195
+ onExpire={() => setRecaptchaVerified(false)}
196
+ />
197
+ </>
198
+ )}
129
199
 
130
200
  <OButton
131
201
  text={emailSent && !formState.result?.error ? t('LINK_SEND_FORGOT_PASSWORD', 'Link Sent') : t('FRONT_RECOVER_PASSWORD', 'Recover Password')}
@@ -146,6 +216,7 @@ const ForgotPasswordUI = (props: any) => {
146
216
  export const ForgotPasswordForm = (props: any) => {
147
217
  const ForgotPasswordProps = {
148
218
  ...props,
219
+ isRecaptchaEnable: true,
149
220
  UIComponent: ForgotPasswordUI
150
221
  }
151
222
  return <ForgotPasswordController {...ForgotPasswordProps} />
@@ -39,7 +39,7 @@ const ORDER_STATUS: any = {
39
39
  23: 'ORDER_DRIVER_ON_WAY'
40
40
  }
41
41
 
42
- const filterSpecialStatus = ['prepared_in', 'delivered_in']
42
+ const filterSpecialStatus = ['prepared_in', 'delivered_in', 'delivery_datetime']
43
43
 
44
44
 
45
45
  const MessagesUI = (props: MessagesParams) => {
@@ -363,6 +363,27 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
363
363
  )
364
364
  }
365
365
 
366
+ const RenderGoogleMap = () => {
367
+ const driverLocationString = typeof order?.driver?.location?.location === 'string' && order?.driver?.location?.location?.split(',').map((l : string) => l.replace(/[^-.0-9]/g, ''))
368
+ const parsedLocations = locations.map(location => typeof location?.location === 'string' ? {
369
+ ...location,
370
+ lat: parseFloat(location?.location?.split(',')[0].replace(/[^-.0-9]/g, '')),
371
+ lng: parseFloat(location?.location?.split(',')[1].replace(/[^-.0-9]/g, ''))
372
+ } : location)
373
+
374
+ return (
375
+ <GoogleMap
376
+ location={typeof order?.driver?.location?.location === 'string'
377
+ ? {
378
+ lat: parseFloat(driverLocationString[0]),
379
+ lng: parseFloat(driverLocationString[1]),
380
+ } : order?.driver?.location
381
+ }
382
+ locations={parsedLocations}
383
+ readOnly
384
+ />
385
+ )
386
+ }
366
387
 
367
388
  useEffect(() => {
368
389
  if (reorderState?.error) {
@@ -683,11 +704,7 @@ export const OrderDetailsUI = (props: OrderDetailsParams) => {
683
704
  <>
684
705
  {order?.driver?.location && mapValidStatuses.includes(parseInt(order?.status)) && (
685
706
  <Map>
686
- <GoogleMap
687
- location={order?.driver?.location}
688
- locations={locations}
689
- readOnly
690
- />
707
+ <RenderGoogleMap />
691
708
  </Map>
692
709
  )}
693
710
  </>
@@ -1,5 +1,5 @@
1
1
  import React, { useState } from 'react'
2
- import { PromotionsController, useLanguage, useUtils, useEvent } from 'ordering-components/native'
2
+ import { PromotionsController, useLanguage, useUtils } from 'ordering-components/native'
3
3
  import {
4
4
  PromotionsContainer,
5
5
  SingleOfferContainer,
@@ -17,9 +17,11 @@ import { useTheme } from 'styled-components/native';
17
17
  import { OButton, OIcon, OModal, OText } from '../shared'
18
18
  import { Placeholder, PlaceholderLine } from 'rn-placeholder'
19
19
  import { NotFoundSource } from '../NotFoundSource'
20
- import { View, StyleSheet, ScrollView } from 'react-native'
20
+ import { View, StyleSheet, ScrollView, Platform } from 'react-native'
21
21
  import FastImage from 'react-native-fast-image'
22
22
  import { PromotionParams } from '../../types'
23
+ import { Container } from '../../layouts/Container'
24
+
23
25
  const PromotionsUI = (props: PromotionParams) => {
24
26
  const {
25
27
  navigation,
@@ -85,139 +87,138 @@ const PromotionsUI = (props: PromotionParams) => {
85
87
  : t('SERVICE_FEE', 'Service fee')
86
88
 
87
89
  return (
88
- <PromotionsContainer>
90
+ <Container noPadding>
89
91
  <NavBar
90
- onActionLeft={() => navigation.goBack()}
91
- btnStyle={{ paddingLeft: 0 }}
92
- paddingTop={20}
93
- style={{ paddingBottom: 0, flexDirection: 'column', alignItems: 'flex-start' }}
94
92
  title={t('PROMOTIONS', 'Promotions')}
95
93
  titleAlign={'center'}
96
- titleStyle={{ fontSize: 16, marginRight: 0, marginLeft: 0, marginBottom: 10 }}
97
- titleWrapStyle={{ paddingHorizontal: 0 }}
94
+ onActionLeft={() => navigation.goBack()}
95
+ showCall={false}
96
+ style={{ paddingVertical: Platform.OS === 'ios' ? 0 : 20, marginLeft: 20 }}
98
97
  />
99
- <SearchBarContainer>
100
- <SearchBar
101
- placeholder={t('SEARCH_OFFERS', 'Search offers')}
102
- onSearch={handleSearchValue}
103
- />
104
- </SearchBarContainer>
98
+ <PromotionsContainer>
99
+ <SearchBarContainer>
100
+ <SearchBar
101
+ placeholder={t('SEARCH_OFFERS', 'Search offers')}
102
+ onSearch={handleSearchValue}
103
+ />
104
+ </SearchBarContainer>
105
105
 
106
- {offersState?.loading && (
107
- <>
108
- {[...Array(5).keys()].map((key, i) => (
109
- <Placeholder key={i} style={{ flexDirection: 'row', marginBottom: 20 }}>
110
- <PlaceholderLine height={10} width={45} />
111
- <PlaceholderLine height={10} width={60} />
112
- <PlaceholderLine height={10} width={75} />
113
- </Placeholder>
114
- ))}
115
- </>
116
- )}
117
- {((!offersState?.loading && filteredOffers?.length === 0) || offersState?.error) && (
118
- <NotFoundSource
119
- content={offersState?.error || t('NOT_FOUND_OFFERS', 'Not found offers')}
120
- />
121
- )}
122
- <ScrollView>
123
- {!offersState?.loading && offersState.offers?.length > 0 && filteredOffers?.map((offer: any) => (
124
- <SingleOfferContainer key={offer.id}>
125
- <OfferInformation>
126
- <OText style={styles.offerTitle} numberOfLines={2}>{offer?.name}</OText>
127
- {offer?.description && (
128
- <OText style={styles.offerDescription} numberOfLines={2}>{offer?.description}</OText>
129
- )}
130
- <OText style={styles.offerExtraInfo}>
131
- {t('EXPIRES', 'Expires')} {parseDate(offer?.end, { outputFormat: 'MMM DD, YYYY' })}
132
- </OText>
133
- <AvailableBusinesses>
134
- <OText style={styles.offerExtraInfo} numberOfLines={1}>
135
- {t('APPLY_FOR', 'Apply for')}:
136
- {offer.businesses.map((business: any, i: number) => (
137
- <React.Fragment key={i}>{' '}{business?.name}{i + 1 < offer.businesses?.length ? ',' : ''}</React.Fragment>
138
- ))}
106
+ {offersState?.loading && (
107
+ <>
108
+ {[...Array(5).keys()].map((key, i) => (
109
+ <Placeholder key={i} style={{ flexDirection: 'row', marginBottom: 20 }}>
110
+ <PlaceholderLine height={10} width={45} />
111
+ <PlaceholderLine height={10} width={60} />
112
+ <PlaceholderLine height={10} width={75} />
113
+ </Placeholder>
114
+ ))}
115
+ </>
116
+ )}
117
+ {((!offersState?.loading && filteredOffers?.length === 0) || offersState?.error) && (
118
+ <NotFoundSource
119
+ content={offersState?.error || t('NOT_FOUND_OFFERS', 'Not found offers')}
120
+ />
121
+ )}
122
+ <ScrollView>
123
+ {!offersState?.loading && offersState.offers?.length > 0 && filteredOffers?.map((offer: any) => (
124
+ <SingleOfferContainer key={offer.id}>
125
+ <OfferInformation>
126
+ <OText style={styles.offerTitle} numberOfLines={2}>{offer?.name}</OText>
127
+ {offer?.description && (
128
+ <OText style={styles.offerDescription} numberOfLines={2}>{offer?.description}</OText>
129
+ )}
130
+ <OText style={styles.offerExtraInfo}>
131
+ {t('EXPIRES', 'Expires')} {parseDate(offer?.end, { outputFormat: 'MMM DD, YYYY' })}
139
132
  </OText>
140
- </AvailableBusinesses>
141
- </OfferInformation>
142
- <OButton
143
- onClick={() => handleClickOffer(offer)}
144
- text={t('VIEW', 'View')}
145
- style={styles.buttonStyle}
146
- textStyle={{ fontSize: 10, color: '#fff', flexWrap: 'nowrap' }}
147
- />
148
- </SingleOfferContainer>
149
- ))}
150
- </ScrollView>
151
- <OModal
152
- open={openModal}
153
- onClose={() => setOpenModal(false)}
154
- entireModal
133
+ <AvailableBusinesses>
134
+ <OText style={styles.offerExtraInfo} numberOfLines={1}>
135
+ {t('APPLY_FOR', 'Apply for')}:
136
+ {offer.businesses.map((business: any, i: number) => (
137
+ <React.Fragment key={i}>{' '}{business?.name}{i + 1 < offer.businesses?.length ? ',' : ''}</React.Fragment>
138
+ ))}
139
+ </OText>
140
+ </AvailableBusinesses>
141
+ </OfferInformation>
142
+ <OButton
143
+ onClick={() => handleClickOffer(offer)}
144
+ text={t('VIEW', 'View')}
145
+ style={styles.buttonStyle}
146
+ textStyle={{ fontSize: 10, color: '#fff', flexWrap: 'nowrap' }}
147
+ />
148
+ </SingleOfferContainer>
149
+ ))}
150
+ </ScrollView>
151
+ <OModal
152
+ open={openModal}
153
+ onClose={() => setOpenModal(false)}
154
+ entireModal
155
155
 
156
- title={``}
157
- >
158
- <View style={{ padding: 20 }}>
159
- <OText style={{ alignSelf: 'center', fontWeight: '700' }} mBottom={20}>
160
- {offerSelected?.name} / {t('VALUE_OF_OFFER', 'Value of offer')}: {offerSelected?.rate_type === 1 ? `${offerSelected?.rate}%` : `${parsePrice(offerSelected?.rate)}`}
161
- </OText>
162
- <OfferData>
163
- {offerSelected?.type === 2 && (
164
- <Code>
165
- <OText>{t('YOUR_CODE', 'Your code')}</OText>
166
- <OText color={theme.colors.primary}>{offerSelected.coupon}</OText>
167
- </Code>
168
- )}
169
- <OText>{t('APPLIES_TO', 'Applies to')}: {targetString}</OText>
170
- {offerSelected?.auto && (
171
- <OText>{t('OFFER_AUTOMATIC', 'This offer applies automatic')}</OText>
172
- )}
173
- {offerSelected?.minimum && (
174
- <OText>{t('MINIMUM_PURCHASE_FOR_OFFER', 'Minimum purchase for use this offer')}: {parsePrice(offerSelected?.minimum)}</OText>
175
- )}
176
- {offerSelected?.max_discount && (
177
- <OText>{t('MAX_DISCOUNT_ALLOWED', 'Max discount allowed')}: {parsePrice(offerSelected?.max_discount)}</OText>
178
- )}
179
- {offerSelected?.description && (
180
- <OText>{offerSelected?.description}</OText>
181
- )}
182
- </OfferData>
183
- <OText style={{ marginTop: 10, marginBottom: 10 }}>
184
- {t('AVAILABLE_BUSINESSES_FOR_OFFER', 'Available businesses for this offer')}:
185
- </OText>
186
- <ScrollView style={{ height: '75%' }}>
187
- {offerSelected?.businesses?.map((business: any) => {
188
- return (
189
- <SingleBusinessOffer key={business.id}>
190
- {business?.logo ? (
191
- <FastImage
192
- style={styles.productStyle}
193
- source={{
194
- uri: optimizeImage(business?.logo, 'h_250,c_limit'),
195
- priority: FastImage.priority.normal,
196
- }}
197
- resizeMode={FastImage.resizeMode.cover}
198
- />
199
- ) : (
200
- <OIcon
201
- src={theme?.images?.dummies?.product}
202
- style={styles.productStyle}
203
- />
204
- )}
205
- <BusinessInfo>
206
- <OText style={{ maxWidth: '60%' }}>{business.name}</OText>
207
- <OButton
208
- onClick={() => handleBusinessClick(business)}
209
- text={t('GO_TO_BUSINESSS', 'Go to business')}
210
- style={styles.modalButtonStyle}
211
- textStyle={{ fontSize: 10, color: '#fff' }}
212
- />
213
- </BusinessInfo>
214
- </SingleBusinessOffer>
215
- )
216
- })}
217
- </ScrollView>
218
- </View>
219
- </OModal>
220
- </PromotionsContainer>
156
+ title={``}
157
+ >
158
+ <View style={{ padding: 20 }}>
159
+ <OText style={{ alignSelf: 'center', fontWeight: '700' }} mBottom={20}>
160
+ {offerSelected?.name} / {t('VALUE_OF_OFFER', 'Value of offer')}: {offerSelected?.rate_type === 1 ? `${offerSelected?.rate}%` : `${parsePrice(offerSelected?.rate)}`}
161
+ </OText>
162
+ <OfferData>
163
+ {offerSelected?.type === 2 && (
164
+ <Code>
165
+ <OText>{t('YOUR_CODE', 'Your code')}</OText>
166
+ <OText color={theme.colors.primary}>{offerSelected.coupon}</OText>
167
+ </Code>
168
+ )}
169
+ <OText>{t('APPLIES_TO', 'Applies to')}: {targetString}</OText>
170
+ {offerSelected?.auto && (
171
+ <OText>{t('OFFER_AUTOMATIC', 'This offer applies automatic')}</OText>
172
+ )}
173
+ {offerSelected?.minimum && (
174
+ <OText>{t('MINIMUM_PURCHASE_FOR_OFFER', 'Minimum purchase for use this offer')}: {parsePrice(offerSelected?.minimum)}</OText>
175
+ )}
176
+ {offerSelected?.max_discount && (
177
+ <OText>{t('MAX_DISCOUNT_ALLOWED', 'Max discount allowed')}: {parsePrice(offerSelected?.max_discount)}</OText>
178
+ )}
179
+ {offerSelected?.description && (
180
+ <OText>{offerSelected?.description}</OText>
181
+ )}
182
+ </OfferData>
183
+ <OText style={{ marginTop: 10, marginBottom: 10 }}>
184
+ {t('AVAILABLE_BUSINESSES_FOR_OFFER', 'Available businesses for this offer')}:
185
+ </OText>
186
+ <ScrollView style={{ height: '75%' }}>
187
+ {offerSelected?.businesses?.map((business: any) => {
188
+ return (
189
+ <SingleBusinessOffer key={business.id}>
190
+ {business?.logo ? (
191
+ <FastImage
192
+ style={styles.productStyle}
193
+ source={{
194
+ uri: optimizeImage(business?.logo, 'h_250,c_limit'),
195
+ priority: FastImage.priority.normal,
196
+ }}
197
+ resizeMode={FastImage.resizeMode.cover}
198
+ />
199
+ ) : (
200
+ <OIcon
201
+ src={theme?.images?.dummies?.product}
202
+ style={styles.productStyle}
203
+ />
204
+ )}
205
+ <BusinessInfo>
206
+ <OText style={{ maxWidth: '60%' }}>{business.name}</OText>
207
+ <OButton
208
+ onClick={() => handleBusinessClick(business)}
209
+ text={t('GO_TO_BUSINESSS', 'Go to business')}
210
+ style={styles.modalButtonStyle}
211
+ textStyle={{ fontSize: 10, color: '#fff' }}
212
+ />
213
+ </BusinessInfo>
214
+ </SingleBusinessOffer>
215
+ )
216
+ })}
217
+ </ScrollView>
218
+ </View>
219
+ </OModal>
220
+ </PromotionsContainer>
221
+ </Container>
221
222
  )
222
223
  }
223
224
 
@@ -2,6 +2,8 @@ import styled, { css } from 'styled-components/native'
2
2
 
3
3
  export const PromotionsContainer = styled.View`
4
4
  width: 100%;
5
+ padding-left: 40px;
6
+ padding-right: 40px;
5
7
  `
6
8
 
7
9
  export const SingleOfferContainer = styled.View`