ordering-ui-react-native 0.15.47 → 0.15.50
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 +2 -1
- package/themes/business/src/components/PreviousOrders/index.tsx +7 -5
- package/themes/original/src/components/LoginForm/index.tsx +73 -2
- package/themes/original/src/components/LoginForm/styles.tsx +6 -1
- package/themes/original/src/components/OrderProgress/index.tsx +1 -1
- package/themes/original/src/components/OrderProgress/styles.tsx +1 -0
- package/themes/original/src/components/Promotions/index.tsx +232 -0
- package/themes/original/src/components/Promotions/styles.tsx +80 -0
- package/themes/original/src/components/UserProfile/index.tsx +8 -1
- package/themes/original/src/components/shared/HeaderTitle.tsx +2 -1
- package/themes/original/src/types/index.tsx +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ordering-ui-react-native",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.50",
|
|
4
4
|
"description": "Reusable components made in react native",
|
|
5
5
|
"main": "src/index.tsx",
|
|
6
6
|
"author": "ordering.inc",
|
|
@@ -99,6 +99,7 @@
|
|
|
99
99
|
"react-native-picker-select": "^8.0.4",
|
|
100
100
|
"react-native-print": "^0.9.0",
|
|
101
101
|
"react-native-reanimated": "^1.13.1",
|
|
102
|
+
"react-native-recaptcha-that-works": "^1.2.0",
|
|
102
103
|
"react-native-restart": "^0.0.22",
|
|
103
104
|
"react-native-safe-area-context": "^3.1.8",
|
|
104
105
|
"react-native-screens": "^2.11.0",
|
|
@@ -102,12 +102,13 @@ export const PreviousOrders = (props: any) => {
|
|
|
102
102
|
const getDelayMinutes = (order: any) => {
|
|
103
103
|
// targetMin = delivery_datetime + eta_time - now()
|
|
104
104
|
const offset = 300
|
|
105
|
-
const cdtToutc =
|
|
105
|
+
const cdtToutc = moment(order?.delivery_datetime).add(offset, 'minutes').format('YYYY-MM-DD HH:mm:ss')
|
|
106
106
|
const _delivery = order?.delivery_datetime_utc
|
|
107
107
|
? parseDate(order?.delivery_datetime_utc)
|
|
108
108
|
: parseDate(cdtToutc)
|
|
109
109
|
const _eta = order?.eta_time
|
|
110
|
-
|
|
110
|
+
const diffTimeAsSeconds = moment(_delivery, 'YYYY-MM-DD hh:mm A').add(_eta, 'minutes').diff(moment().utc(), 'seconds')
|
|
111
|
+
return Math.ceil(diffTimeAsSeconds / 60)
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
const displayDelayedTime = (order: any) => {
|
|
@@ -128,9 +129,10 @@ export const PreviousOrders = (props: any) => {
|
|
|
128
129
|
return finalTaget
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
const getStatusClassName = (minutes:
|
|
132
|
-
if (isNaN(Number(minutes))) return
|
|
133
|
-
|
|
132
|
+
const getStatusClassName = (minutes: number) => {
|
|
133
|
+
if (isNaN(Number(minutes))) return 'in_time'
|
|
134
|
+
const delayTime = configState?.configs?.order_deadlines_delayed_time?.value
|
|
135
|
+
return minutes > 0 ? 'in_time' : Math.abs(minutes) <= delayTime ? 'at_risk' : 'delayed'
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
useEffect(() => {
|
|
@@ -4,6 +4,7 @@ import Spinner from 'react-native-loading-spinner-overlay';
|
|
|
4
4
|
import { useForm, Controller } from 'react-hook-form';
|
|
5
5
|
import { PhoneInputNumber } from '../PhoneInputNumber';
|
|
6
6
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
7
|
+
import Recaptcha from 'react-native-recaptcha-that-works'
|
|
7
8
|
|
|
8
9
|
import {
|
|
9
10
|
LoginForm as LoginFormController,
|
|
@@ -18,7 +19,6 @@ import { FacebookLogin } from '../FacebookLogin';
|
|
|
18
19
|
import { VerifyPhone } from '../../../../../src/components/VerifyPhone';
|
|
19
20
|
import { OModal } from '../../../../../src/components/shared';
|
|
20
21
|
|
|
21
|
-
|
|
22
22
|
import {
|
|
23
23
|
Container,
|
|
24
24
|
ButtonsWrapper,
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
LineSeparator,
|
|
33
33
|
SkeletonWrapper,
|
|
34
34
|
TabBtn,
|
|
35
|
+
RecaptchaButton
|
|
35
36
|
} from './styles';
|
|
36
37
|
|
|
37
38
|
import NavBar from '../NavBar';
|
|
@@ -60,7 +61,9 @@ const LoginFormUI = (props: LoginParams) => {
|
|
|
60
61
|
handleSendVerifyCode,
|
|
61
62
|
handleCheckPhoneCode,
|
|
62
63
|
onNavigationRedirect,
|
|
63
|
-
notificationState
|
|
64
|
+
notificationState,
|
|
65
|
+
handleReCaptcha,
|
|
66
|
+
enableReCaptcha
|
|
64
67
|
} = props;
|
|
65
68
|
|
|
66
69
|
const [, { showToast }] = useToast();
|
|
@@ -79,6 +82,8 @@ const LoginFormUI = (props: LoginParams) => {
|
|
|
79
82
|
cellphone: null,
|
|
80
83
|
},
|
|
81
84
|
});
|
|
85
|
+
const [recaptchaConfig, setRecaptchaConfig] = useState<any>({})
|
|
86
|
+
const [recaptchaVerified, setRecaptchaVerified] = useState(false)
|
|
82
87
|
|
|
83
88
|
const theme = useTheme();
|
|
84
89
|
|
|
@@ -100,10 +105,15 @@ const LoginFormUI = (props: LoginParams) => {
|
|
|
100
105
|
flexGrow: 1,
|
|
101
106
|
marginBottom: 7,
|
|
102
107
|
},
|
|
108
|
+
recaptchaIcon: {
|
|
109
|
+
width: 100,
|
|
110
|
+
height: 100,
|
|
111
|
+
}
|
|
103
112
|
});
|
|
104
113
|
|
|
105
114
|
const emailRef = useRef<any>({});
|
|
106
115
|
const passwordRef = useRef<any>({});
|
|
116
|
+
const recaptchaRef = useRef<any>({});
|
|
107
117
|
|
|
108
118
|
const handleChangeTab = (val: string) => {
|
|
109
119
|
props.handleChangeTab(val);
|
|
@@ -156,6 +166,33 @@ const LoginFormUI = (props: LoginParams) => {
|
|
|
156
166
|
onChange(value.toLowerCase().replace(/[&,()%";:ç?<>{}\\[\]\s]/g, ''));
|
|
157
167
|
};
|
|
158
168
|
|
|
169
|
+
const handleOpenRecaptcha = () => {
|
|
170
|
+
setRecaptchaVerified(false)
|
|
171
|
+
if (!recaptchaConfig?.siteKey) {
|
|
172
|
+
showToast(ToastType.Error, t('NO_RECAPTCHA_SITE_KEY', 'The config doesn\'t have recaptcha site key'));
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
if (!recaptchaConfig?.baseUrl) {
|
|
176
|
+
showToast(ToastType.Error, t('NO_RECAPTCHA_BASE_URL', 'The config doesn\'t have recaptcha base url'));
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
recaptchaRef.current.open()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const onRecaptchaVerify = (token: any) => {
|
|
183
|
+
setRecaptchaVerified(true)
|
|
184
|
+
handleReCaptcha(token)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
if (configs && Object.keys(configs).length > 0 && enableReCaptcha) {
|
|
189
|
+
setRecaptchaConfig({
|
|
190
|
+
siteKey: configs?.security_recaptcha_site_key?.value || null,
|
|
191
|
+
baseUrl: configs?.security_recaptcha_base_url?.value || null
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
}, [configs, enableReCaptcha])
|
|
195
|
+
|
|
159
196
|
useEffect(() => {
|
|
160
197
|
if (!formState.loading && formState.result?.error) {
|
|
161
198
|
formState.result?.result &&
|
|
@@ -399,6 +436,39 @@ const LoginFormUI = (props: LoginParams) => {
|
|
|
399
436
|
</OText>
|
|
400
437
|
</TouchableOpacity>
|
|
401
438
|
)}
|
|
439
|
+
|
|
440
|
+
{enableReCaptcha && (
|
|
441
|
+
<>
|
|
442
|
+
<TouchableOpacity
|
|
443
|
+
onPress={handleOpenRecaptcha}
|
|
444
|
+
>
|
|
445
|
+
<RecaptchaButton>
|
|
446
|
+
{recaptchaVerified ? (
|
|
447
|
+
<MaterialCommunityIcons
|
|
448
|
+
name="checkbox-marked"
|
|
449
|
+
size={26}
|
|
450
|
+
color={theme.colors.primary}
|
|
451
|
+
/>
|
|
452
|
+
) : (
|
|
453
|
+
<MaterialCommunityIcons
|
|
454
|
+
name="checkbox-blank-outline"
|
|
455
|
+
size={26}
|
|
456
|
+
color={theme.colors.mediumGray}
|
|
457
|
+
/>
|
|
458
|
+
)}
|
|
459
|
+
<OText size={14} mLeft={8}>{t('VERIFY_ReCAPTCHA', 'Verify reCAPTCHA')}</OText>
|
|
460
|
+
</RecaptchaButton>
|
|
461
|
+
</TouchableOpacity>
|
|
462
|
+
<Recaptcha
|
|
463
|
+
ref={recaptchaRef}
|
|
464
|
+
siteKey={recaptchaConfig?.siteKey}
|
|
465
|
+
baseUrl={recaptchaConfig?.baseUrl}
|
|
466
|
+
onVerify={onRecaptchaVerify}
|
|
467
|
+
onExpire={() => setRecaptchaVerified(false)}
|
|
468
|
+
/>
|
|
469
|
+
</>
|
|
470
|
+
)}
|
|
471
|
+
|
|
402
472
|
<OButton
|
|
403
473
|
onClick={handleSubmit(onSubmit)}
|
|
404
474
|
text={loginButtonText}
|
|
@@ -559,6 +629,7 @@ const LoginFormUI = (props: LoginParams) => {
|
|
|
559
629
|
export const LoginForm = (props: any) => {
|
|
560
630
|
const loginProps = {
|
|
561
631
|
...props,
|
|
632
|
+
isRecaptchaEnable: true,
|
|
562
633
|
UIComponent: LoginFormUI,
|
|
563
634
|
};
|
|
564
635
|
return <LoginFormController {...loginProps} />;
|
|
@@ -36,7 +36,7 @@ export const LoginWith = styled.View`
|
|
|
36
36
|
width: 100%;
|
|
37
37
|
align-items: flex-start;
|
|
38
38
|
border-bottom-width: 1px;
|
|
39
|
-
border-bottom-color: ${(props: any) => props.theme.colors.border}
|
|
39
|
+
border-bottom-color: ${(props: any) => props.theme.colors.border};
|
|
40
40
|
`;
|
|
41
41
|
|
|
42
42
|
export const FormInput = styled.View`
|
|
@@ -80,3 +80,8 @@ export const LineSeparator = styled.View`
|
|
|
80
80
|
export const SkeletonWrapper = styled.View`
|
|
81
81
|
width: 90%;
|
|
82
82
|
`
|
|
83
|
+
export const RecaptchaButton = styled.View`
|
|
84
|
+
flex-direction: row;
|
|
85
|
+
align-items: center;
|
|
86
|
+
margin-bottom: 10px;
|
|
87
|
+
`
|
|
@@ -188,7 +188,7 @@ const OrderProgressUI = (props: any) => {
|
|
|
188
188
|
<ProgressBar style={{ width: getOrderStatus(lastOrder.status)?.percentage ? `${getOrderStatus(lastOrder.status)?.percentage}%` : '0%' }} />
|
|
189
189
|
</ProgressContentWrapper>
|
|
190
190
|
<ProgressTextWrapper>
|
|
191
|
-
<OText size={12}>{getOrderStatus(lastOrder.status)?.value}</OText>
|
|
191
|
+
<OText size={12} style={{ width: '50%' }}>{getOrderStatus(lastOrder.status)?.value}</OText>
|
|
192
192
|
<TimeWrapper>
|
|
193
193
|
<OText size={11}>{t('ESTIMATED_DELIVERY', 'Estimated delivery')}</OText>
|
|
194
194
|
<OText size={11}>
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { PromotionsController, useLanguage, useUtils, useEvent } from 'ordering-components/native'
|
|
3
|
+
import {
|
|
4
|
+
PromotionsContainer,
|
|
5
|
+
SingleOfferContainer,
|
|
6
|
+
OfferInformation,
|
|
7
|
+
SearchBarContainer,
|
|
8
|
+
SingleBusinessOffer,
|
|
9
|
+
AvailableBusinesses,
|
|
10
|
+
OfferData,
|
|
11
|
+
Code,
|
|
12
|
+
BusinessInfo
|
|
13
|
+
} from './styles'
|
|
14
|
+
import { SearchBar } from '../SearchBar'
|
|
15
|
+
import NavBar from '../NavBar'
|
|
16
|
+
import { useTheme } from 'styled-components/native';
|
|
17
|
+
import { OButton, OIcon, OModal, OText } from '../shared'
|
|
18
|
+
import { Placeholder, PlaceholderLine } from 'rn-placeholder'
|
|
19
|
+
import { NotFoundSource } from '../NotFoundSource'
|
|
20
|
+
import { View, StyleSheet, ScrollView } from 'react-native'
|
|
21
|
+
import FastImage from 'react-native-fast-image'
|
|
22
|
+
import { PromotionParams } from '../../types'
|
|
23
|
+
const PromotionsUI = (props : PromotionParams) => {
|
|
24
|
+
const {
|
|
25
|
+
navigation,
|
|
26
|
+
offersState,
|
|
27
|
+
handleSearchValue,
|
|
28
|
+
searchValue,
|
|
29
|
+
offerSelected,
|
|
30
|
+
setOfferSelected
|
|
31
|
+
} = props
|
|
32
|
+
|
|
33
|
+
const theme = useTheme();
|
|
34
|
+
|
|
35
|
+
const styles = StyleSheet.create({
|
|
36
|
+
productStyle: {
|
|
37
|
+
width: 75,
|
|
38
|
+
height: 75,
|
|
39
|
+
borderRadius: 7.6
|
|
40
|
+
},
|
|
41
|
+
buttonStyle: {
|
|
42
|
+
width: 55,
|
|
43
|
+
height: 25,
|
|
44
|
+
paddingLeft: 0,
|
|
45
|
+
paddingRight: 0
|
|
46
|
+
},
|
|
47
|
+
offerTitle: {
|
|
48
|
+
fontSize: 12
|
|
49
|
+
},
|
|
50
|
+
offerDescription: {
|
|
51
|
+
color: '#909BA9',
|
|
52
|
+
fontSize: 10
|
|
53
|
+
},
|
|
54
|
+
offerExtraInfo: {
|
|
55
|
+
fontSize: 10
|
|
56
|
+
},
|
|
57
|
+
modalButtonStyle: {
|
|
58
|
+
width: 100,
|
|
59
|
+
height: 35,
|
|
60
|
+
paddingLeft: 0,
|
|
61
|
+
paddingRight: 0,
|
|
62
|
+
borderRadius: 7.6
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const [, t] = useLanguage()
|
|
67
|
+
const [{ parseDate, parsePrice, optimizeImage }] = useUtils()
|
|
68
|
+
const [events] = useEvent()
|
|
69
|
+
const [openModal, setOpenModal] = useState(false)
|
|
70
|
+
|
|
71
|
+
const handleClickOffer = (offer : any) => {
|
|
72
|
+
setOpenModal(true)
|
|
73
|
+
setOfferSelected(offer)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const handleBusinessClick = (business : any) => {
|
|
77
|
+
events.emit('go_to_page', { page: 'business', params: { store: business.slug } })
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const filteredOffers = offersState?.offers?.filter((offer : any) => offer.name.toLowerCase().includes(searchValue.toLowerCase()))
|
|
81
|
+
|
|
82
|
+
const targetString = offerSelected?.target === 1
|
|
83
|
+
? t('SUBTOTAL', 'Subtotal')
|
|
84
|
+
: offerSelected?.target === 2
|
|
85
|
+
? t('DELIVERY_FEE', 'Delivery fee')
|
|
86
|
+
: t('SERVICE_FEE', 'Service fee')
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<PromotionsContainer>
|
|
90
|
+
<NavBar
|
|
91
|
+
onActionLeft={() => navigation.goBack()}
|
|
92
|
+
btnStyle={{ paddingLeft: 0 }}
|
|
93
|
+
paddingTop={20}
|
|
94
|
+
style={{ paddingBottom: 0, flexDirection: 'column', alignItems: 'flex-start' }}
|
|
95
|
+
title={t('PROMOTIONS', 'Promotions')}
|
|
96
|
+
titleAlign={'center'}
|
|
97
|
+
titleStyle={{ fontSize: 16, marginRight: 0, marginLeft: 0, marginBottom: 10 }}
|
|
98
|
+
titleWrapStyle={{ paddingHorizontal: 0 }}
|
|
99
|
+
/>
|
|
100
|
+
<SearchBarContainer>
|
|
101
|
+
<SearchBar
|
|
102
|
+
placeholder={t('SEARCH_OFFERS', 'Search offers')}
|
|
103
|
+
onSearch={handleSearchValue}
|
|
104
|
+
/>
|
|
105
|
+
</SearchBarContainer>
|
|
106
|
+
|
|
107
|
+
{offersState?.loading && (
|
|
108
|
+
<>
|
|
109
|
+
{[...Array(5).keys()].map((key, i) => (
|
|
110
|
+
<Placeholder key={i} style={{ flexDirection: 'row', marginBottom: 20 }}>
|
|
111
|
+
<PlaceholderLine height={10} width={45} />
|
|
112
|
+
<PlaceholderLine height={10} width={60} />
|
|
113
|
+
<PlaceholderLine height={10} width={75} />
|
|
114
|
+
</Placeholder>
|
|
115
|
+
))}
|
|
116
|
+
</>
|
|
117
|
+
)}
|
|
118
|
+
{((!offersState?.loading && filteredOffers?.length === 0) || offersState?.error) && (
|
|
119
|
+
<NotFoundSource
|
|
120
|
+
content={offersState?.error || t('NOT_FOUND_OFFERS', 'Not found offers')}
|
|
121
|
+
/>
|
|
122
|
+
)}
|
|
123
|
+
<ScrollView>
|
|
124
|
+
{!offersState?.loading && offersState.offers?.length > 0 && filteredOffers?.map((offer : any) => (
|
|
125
|
+
<SingleOfferContainer key={offer.id}>
|
|
126
|
+
<OfferInformation>
|
|
127
|
+
<OText style={styles.offerTitle}>{offer?.name}</OText>
|
|
128
|
+
{offer?.description && (
|
|
129
|
+
<OText style={styles.offerDescription}>{offer?.description}</OText>
|
|
130
|
+
)}
|
|
131
|
+
<OText style={styles.offerExtraInfo}>
|
|
132
|
+
{t('EXPIRES', 'Expires')} {parseDate(offer?.end, { outputFormat: 'MMM DD, YYYY' })}
|
|
133
|
+
</OText>
|
|
134
|
+
<AvailableBusinesses>
|
|
135
|
+
<OText style={styles.offerExtraInfo}>{t('APPLY_FOR', 'Apply for')}:</OText>
|
|
136
|
+
{offer.businesses.map((business: any, i: number) => (
|
|
137
|
+
<OText style={styles.offerExtraInfo} key={business?.id}>{' '}{business?.name}{i + 1 < offer.businesses?.length ? ',' : ''}</OText>
|
|
138
|
+
))}
|
|
139
|
+
</AvailableBusinesses>
|
|
140
|
+
</OfferInformation>
|
|
141
|
+
<OButton
|
|
142
|
+
onClick={() => handleClickOffer(offer)}
|
|
143
|
+
text={t('VIEW', 'View')}
|
|
144
|
+
style={styles.buttonStyle}
|
|
145
|
+
textStyle={{ fontSize: 10, color: '#fff', flexWrap: 'nowrap' }}
|
|
146
|
+
/>
|
|
147
|
+
</SingleOfferContainer>
|
|
148
|
+
))}
|
|
149
|
+
</ScrollView>
|
|
150
|
+
<OModal
|
|
151
|
+
open={openModal}
|
|
152
|
+
onClose={() => setOpenModal(false)}
|
|
153
|
+
entireModal
|
|
154
|
+
|
|
155
|
+
title={``}
|
|
156
|
+
>
|
|
157
|
+
<View style={{ padding: 20 }}>
|
|
158
|
+
<OText style={{ alignSelf: 'center', fontWeight: '700' }} mBottom={20}>
|
|
159
|
+
{offerSelected?.name} / {t('VALUE_OF_OFFER', 'Value of offer')}: {offerSelected?.rate_type === 1 ? `${offerSelected?.rate}%` : `${parsePrice(offerSelected?.rate)}`}
|
|
160
|
+
</OText>
|
|
161
|
+
<OfferData>
|
|
162
|
+
{offerSelected?.type === 2 && (
|
|
163
|
+
<Code>
|
|
164
|
+
<OText>{t('YOUR_CODE', 'Your code')}</OText>
|
|
165
|
+
<OText color={theme.colors.primary}>{offerSelected.coupon}</OText>
|
|
166
|
+
</Code>
|
|
167
|
+
)}
|
|
168
|
+
<OText>{t('APPLIES_TO', 'Applies to')}: {targetString}</OText>
|
|
169
|
+
{offerSelected?.auto && (
|
|
170
|
+
<OText>{t('OFFER_AUTOMATIC', 'This offer applies automatic')}</OText>
|
|
171
|
+
)}
|
|
172
|
+
{offerSelected?.minimum && (
|
|
173
|
+
<OText>{t('MINIMUM_PURCHASE_FOR_OFFER', 'Minimum purshase for use this offer')}: {parsePrice(offerSelected?.minimum)}</OText>
|
|
174
|
+
)}
|
|
175
|
+
{offerSelected?.max_discount && (
|
|
176
|
+
<OText>{t('MAX_DISCOUNT_ALLOWED', 'Max discount allowed')}: {parsePrice(offerSelected?.max_discount)}</OText>
|
|
177
|
+
)}
|
|
178
|
+
{offerSelected?.description && (
|
|
179
|
+
<OText>{offerSelected?.description}</OText>
|
|
180
|
+
)}
|
|
181
|
+
</OfferData>
|
|
182
|
+
<OText style={{ marginTop: 10, marginBottom: 10 }}>
|
|
183
|
+
{t('AVAILABLE_BUSINESSES_FOR_OFFER', 'Available businesses for this offer')}:
|
|
184
|
+
</OText>
|
|
185
|
+
<ScrollView style={{height: '75%'}}>
|
|
186
|
+
{offerSelected?.businesses?.map((business : any) => {
|
|
187
|
+
return (
|
|
188
|
+
<SingleBusinessOffer key={business.id}>
|
|
189
|
+
{business?.logo ? (
|
|
190
|
+
<FastImage
|
|
191
|
+
style={styles.productStyle}
|
|
192
|
+
source={{
|
|
193
|
+
uri: optimizeImage(business?.logo, 'h_250,c_limit'),
|
|
194
|
+
priority: FastImage.priority.normal,
|
|
195
|
+
}}
|
|
196
|
+
resizeMode={FastImage.resizeMode.cover}
|
|
197
|
+
/>
|
|
198
|
+
) : (
|
|
199
|
+
<OIcon
|
|
200
|
+
src={theme?.images?.dummies?.product}
|
|
201
|
+
style={styles.productStyle}
|
|
202
|
+
/>
|
|
203
|
+
)}
|
|
204
|
+
<BusinessInfo>
|
|
205
|
+
<OText>{business.name}</OText>
|
|
206
|
+
<OButton
|
|
207
|
+
onClick={() => handleBusinessClick(business)}
|
|
208
|
+
text={t('GO_TO_BUSINESSS', 'Go to business')}
|
|
209
|
+
style={styles.modalButtonStyle}
|
|
210
|
+
textStyle={{ fontSize: 10, color: '#fff' }}
|
|
211
|
+
/>
|
|
212
|
+
</BusinessInfo>
|
|
213
|
+
</SingleBusinessOffer>
|
|
214
|
+
)
|
|
215
|
+
})}
|
|
216
|
+
</ScrollView>
|
|
217
|
+
</View>
|
|
218
|
+
</OModal>
|
|
219
|
+
</PromotionsContainer>
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export const Promotions = (props : PromotionParams) => {
|
|
224
|
+
const PromotionsProps = {
|
|
225
|
+
...props,
|
|
226
|
+
UIComponent: PromotionsUI
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<PromotionsController {...PromotionsProps} />
|
|
231
|
+
)
|
|
232
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components/native'
|
|
2
|
+
|
|
3
|
+
export const PromotionsContainer = styled.View`
|
|
4
|
+
width: 100%;
|
|
5
|
+
`
|
|
6
|
+
|
|
7
|
+
export const SingleOfferContainer = styled.View`
|
|
8
|
+
flex-direction: row;
|
|
9
|
+
width: 100%;
|
|
10
|
+
height: 80px;
|
|
11
|
+
justify-content: space-between;
|
|
12
|
+
align-items: center;
|
|
13
|
+
margin-bottom: 20px;
|
|
14
|
+
|
|
15
|
+
`
|
|
16
|
+
|
|
17
|
+
export const OfferInformation = styled.View`
|
|
18
|
+
justify-content: space-between;
|
|
19
|
+
max-width: 75%;
|
|
20
|
+
`
|
|
21
|
+
|
|
22
|
+
export const SearchBarContainer = styled.View`
|
|
23
|
+
display: flex;
|
|
24
|
+
width: 100%;
|
|
25
|
+
justify-content: flex-start;
|
|
26
|
+
margin-bottom: 20px;
|
|
27
|
+
.search-bar {
|
|
28
|
+
justify-content: flex-start;
|
|
29
|
+
input {
|
|
30
|
+
width: 100%;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
.clear {
|
|
34
|
+
right: 0;
|
|
35
|
+
}
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
export const SingleBusinessOffer = styled.View`
|
|
39
|
+
flex-direction: row;
|
|
40
|
+
`
|
|
41
|
+
|
|
42
|
+
export const AvailableBusinesses = styled.View`
|
|
43
|
+
flex-direction: row;
|
|
44
|
+
overflow: hidden;
|
|
45
|
+
`
|
|
46
|
+
|
|
47
|
+
export const OfferData = styled.View`
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
p{
|
|
52
|
+
color: #909BA9;
|
|
53
|
+
margin: 3px;
|
|
54
|
+
font-size: 14px;
|
|
55
|
+
}
|
|
56
|
+
`
|
|
57
|
+
|
|
58
|
+
export const Code = styled.View`
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
align-items: center;
|
|
62
|
+
margin-bottom: 10px;
|
|
63
|
+
`
|
|
64
|
+
|
|
65
|
+
export const ValueOfOffer = styled.View`
|
|
66
|
+
p{
|
|
67
|
+
font-size: 16px;
|
|
68
|
+
}
|
|
69
|
+
span{
|
|
70
|
+
font-size: 20px;
|
|
71
|
+
}
|
|
72
|
+
`
|
|
73
|
+
|
|
74
|
+
export const BusinessInfo = styled.View`
|
|
75
|
+
flex: 1;
|
|
76
|
+
flex-direction: row;
|
|
77
|
+
justify-content: space-between;
|
|
78
|
+
align-items: center;
|
|
79
|
+
margin-left: 10px;
|
|
80
|
+
`
|
|
@@ -16,6 +16,7 @@ import { LogoutButton } from '../LogoutButton'
|
|
|
16
16
|
import { LanguageSelector } from '../LanguageSelector'
|
|
17
17
|
import MessageCircle from 'react-native-vector-icons/AntDesign'
|
|
18
18
|
import Ionicons from 'react-native-vector-icons/Ionicons'
|
|
19
|
+
import MaterialIcons from 'react-native-vector-icons/MaterialIcons'
|
|
19
20
|
import FastImage from 'react-native-fast-image'
|
|
20
21
|
|
|
21
22
|
import {
|
|
@@ -101,7 +102,7 @@ const ProfileListUI = (props: ProfileParams) => {
|
|
|
101
102
|
const { top, bottom } = useSafeAreaInsets();
|
|
102
103
|
|
|
103
104
|
const isWalletEnabled = configs?.wallet_enabled?.value === '1' && (configs?.wallet_cash_enabled?.value === '1' || configs?.wallet_credit_point_enabled?.value === '1')
|
|
104
|
-
|
|
105
|
+
const IsPromotionsEnabled = configs?.advanced_offers_module === '1' || configs?.advanced_offers_module === 'true'
|
|
105
106
|
const onRedirect = (route: string, params?: any) => {
|
|
106
107
|
navigation.navigate(route, params)
|
|
107
108
|
}
|
|
@@ -178,6 +179,12 @@ const ProfileListUI = (props: ProfileParams) => {
|
|
|
178
179
|
<OText size={14} lineHeight={24} weight={'400'} color={theme.colors.textNormal}>{t('WALLETS', 'Wallets')}</OText>
|
|
179
180
|
</ListItem>
|
|
180
181
|
)}
|
|
182
|
+
{IsPromotionsEnabled && (
|
|
183
|
+
<ListItem onPress={() => onRedirect('Promotions', { isFromProfile: true, isGoBack: true })} activeOpacity={0.7}>
|
|
184
|
+
<MaterialIcons name='local-offer' style={styles.messageIconStyle} color={theme.colors.textNormal} />
|
|
185
|
+
<OText size={14} lineHeight={24} weight={'400'} color={theme.colors.textNormal}>{t('PROMOTIONS', 'Promotions')}</OText>
|
|
186
|
+
</ListItem>
|
|
187
|
+
)}
|
|
181
188
|
<ListItem onPress={() => navigation.navigate('Help', {})} activeOpacity={0.7}>
|
|
182
189
|
<OIcon src={theme.images.general.ic_help} width={16} color={theme.colors.textNormal} style={{ marginEnd: 14 }} />
|
|
183
190
|
<OText size={14} lineHeight={24} weight={'400'} color={theme.colors.textNormal}>{t('HELP', 'Help')}</OText>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
2
3
|
import OText from './OText';
|
|
3
4
|
|
|
4
5
|
const HeaderTitle = (props: any) => {
|
|
@@ -7,7 +8,7 @@ const HeaderTitle = (props: any) => {
|
|
|
7
8
|
<OText
|
|
8
9
|
size={24}
|
|
9
10
|
style={style ?? {
|
|
10
|
-
marginTop: 30,
|
|
11
|
+
marginTop: Platform.OS === 'android' ? 50 : 30,
|
|
11
12
|
paddingHorizontal: 40,
|
|
12
13
|
textTransform: 'capitalize'
|
|
13
14
|
}}
|
|
@@ -17,6 +17,8 @@ export interface LoginParams {
|
|
|
17
17
|
handleSendVerifyCode?: any;
|
|
18
18
|
handleCheckPhoneCode?: any;
|
|
19
19
|
notificationState?: any;
|
|
20
|
+
handleReCaptcha?: any;
|
|
21
|
+
enableReCaptcha?: any;
|
|
20
22
|
}
|
|
21
23
|
export interface ProfileParams {
|
|
22
24
|
navigation?: any;
|
|
@@ -566,3 +568,12 @@ export interface PlaceSpotParams {
|
|
|
566
568
|
getPlacesList?: any,
|
|
567
569
|
setOpenPlaceModal?: any
|
|
568
570
|
}
|
|
571
|
+
|
|
572
|
+
export interface PromotionParams {
|
|
573
|
+
navigation: any,
|
|
574
|
+
offersState: any,
|
|
575
|
+
handleSearchValue: any,
|
|
576
|
+
searchValue: string,
|
|
577
|
+
offerSelected: any,
|
|
578
|
+
setOfferSelected: any,
|
|
579
|
+
}
|