ordering-ui-react-native 0.14.53 → 0.14.56

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.14.53",
3
+ "version": "0.14.56",
4
4
  "description": "Reusable components made in react native",
5
5
  "main": "src/index.tsx",
6
6
  "author": "ordering.inc",
@@ -33,19 +33,33 @@ const StripeElementsFormUI = (props: any) => {
33
33
  const { confirmSetupIntent, loading: confirmSetupLoading } = useConfirmSetupIntent();
34
34
  const [createPmLoading, setCreatePmLoading] = useState(false);
35
35
 
36
- const billingDetails = {
37
- name: `${user.name} ${user.lastname}`,
38
- email: user.email,
39
- address: user.address
40
- };
36
+ let billingDetails: any = {}
37
+
38
+ if (user?.name || user?.lastname) {
39
+ if (user?.name) {
40
+ billingDetails.name = user?.name
41
+ }
42
+ if (user?.lastname) {
43
+ billingDetails.name = `${billingDetails?.name} ${user?.lastname}`
44
+ }
45
+ }
46
+
47
+ if (user?.email) {
48
+ billingDetails.email = user?.email
49
+ }
50
+
51
+ if (user?.address) {
52
+ billingDetails.addressLine1 = user?.address
53
+ }
41
54
 
42
55
  const createPayMethod = async () => {
56
+ const params: any = { type: 'Card' }
57
+ if (Object.keys(billingDetails).length > 0) {
58
+ params.billingDetails = billingDetails
59
+ }
43
60
  try {
44
61
  setCreatePmLoading(true)
45
- const { paymentMethod } = await createPaymentMethod({
46
- type: 'Card',
47
- billingDetails,
48
- });
62
+ const { paymentMethod } = await createPaymentMethod(params);
49
63
 
50
64
  setCreatePmLoading(false)
51
65
  handleSource && handleSource({
@@ -68,11 +82,12 @@ const StripeElementsFormUI = (props: any) => {
68
82
  createPayMethod();
69
83
  return
70
84
  }
85
+ const params: any = { type: 'Card' }
86
+ if (Object.keys(billingDetails).length > 0) {
87
+ params.billingDetails = billingDetails
88
+ }
71
89
  try {
72
- const { setupIntent, error } = await confirmSetupIntent(requirements, {
73
- type: 'Card',
74
- billingDetails,
75
- });
90
+ const { setupIntent, error } = await confirmSetupIntent(requirements, params);
76
91
 
77
92
  if (setupIntent?.status === 'Succeeded') {
78
93
  stripeTokenHandler(setupIntent?.paymentMethodId, user, businessId);
@@ -36,7 +36,7 @@ export const TaxInformation = (props: taxInformationParams) => {
36
36
  ? (product.tax?.id ? product.tax?.id === data?.id : product.tax?.id === null && data?.id === null)
37
37
  : type === 'fee'
38
38
  ? (product.fee?.id ? product.fee?.id === data?.id : (product.fee?.id === null && data?.id === null))
39
- : Object.keys(data?.discounts ?? {}).map(code => code.includes(product?.code))
39
+ : Object.keys(data?.discounts ?? {}).map(code => code.includes(product?.code)) && product?.offers?.find(offer => offer?.name === data?.name)
40
40
  )
41
41
  }
42
42
 
@@ -32,6 +32,7 @@ import { Wallets } from './src/components/Wallets';
32
32
  import { PaymentOptionWallet } from './src/components/PaymentOptionWallet';
33
33
  import { ProductForm } from './src/components/ProductForm';
34
34
  import { UpsellingProducts } from './src/components/UpsellingProducts';
35
+ import { VerifyEmail } from './src/components/VerifyEmail';
35
36
 
36
37
  import { Toast } from './src/components/shared/OToast';
37
38
  import {
@@ -94,6 +95,7 @@ export {
94
95
  PaymentOptionWallet,
95
96
  ProductForm,
96
97
  UpsellingProducts,
98
+ VerifyEmail,
97
99
 
98
100
  // OComponents
99
101
  Toast,
@@ -25,7 +25,7 @@ import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome5';
25
25
  import FastImage from 'react-native-fast-image'
26
26
 
27
27
  export const BusinessControllerUI = (props: BusinessControllerParams) => {
28
- const { business, handleClick, navigation } = props;
28
+ const { business, handleClick, navigation, isBusinessOpen } = props;
29
29
  const [{ parsePrice, parseDistance, parseNumber, optimizeImage }] =
30
30
  useUtils();
31
31
  const [orderState] = useOrder();
@@ -122,7 +122,7 @@ export const BusinessControllerUI = (props: BusinessControllerParams) => {
122
122
  </View>
123
123
  )}
124
124
  <BusinessState>
125
- {!business?.open && (
125
+ {!isBusinessOpen && (
126
126
  <View style={styles.businessStateView}>
127
127
  <OText
128
128
  color={theme.colors.textThird}
@@ -168,7 +168,7 @@ export const BusinessControllerUI = (props: BusinessControllerParams) => {
168
168
  <OText>{getBusinessType()}</OText>
169
169
  </BusinessCategory> */}
170
170
  <Metadata>
171
- {!business?.open ? (
171
+ {!isBusinessOpen ? (
172
172
  <View style={styles.closed}>
173
173
  <OText size={10} color={theme.colors.red}>
174
174
  {t('CLOSED', 'Closed')}
@@ -23,7 +23,7 @@ import {
23
23
  } from './styles';
24
24
 
25
25
  export const BusinessFeaturedCtrlUI = (props: BusinessControllerParams) => {
26
- const { business, handleClick } = props;
26
+ const { business, handleClick, isBusinessOpen } = props;
27
27
  const [{ parsePrice, parseDistance, parseNumber, optimizeImage }] =
28
28
  useUtils();
29
29
  const [orderState] = useOrder();
@@ -128,7 +128,7 @@ export const BusinessFeaturedCtrlUI = (props: BusinessControllerParams) => {
128
128
  <OText>{getBusinessType()}</OText>
129
129
  </BusinessCategory> */}
130
130
  <Metadata>
131
- {!business?.open ? (
131
+ {!isBusinessOpen ? (
132
132
  <View style={styles.closed}>
133
133
  <OText size={10} color={theme.colors.red}>
134
134
  {t('CLOSED', 'Closed')}
@@ -177,11 +177,8 @@ const BusinessesListingUI = (props: BusinessesListingParams) => {
177
177
  // }, [])
178
178
 
179
179
  const handleOnRefresh = () => {
180
- const hasMore = !(
181
- paginationProps.totalPages === paginationProps.currentPage
182
- );
183
- if (!businessesList.loading && hasMore) {
184
- getBusinesses();
180
+ if (!businessesList.loading) {
181
+ getBusinesses(true);
185
182
  }
186
183
  }
187
184
 
@@ -315,6 +312,7 @@ const BusinessesListingUI = (props: BusinessesListingParams) => {
315
312
  <BusinessFeaturedController
316
313
  key={bAry[0].id}
317
314
  business={bAry[0]}
315
+ isBusinessOpen={bAry[0]?.open}
318
316
  handleCustomClick={handleBusinessClick}
319
317
  orderType={orderState?.options?.type}
320
318
  />
@@ -322,6 +320,7 @@ const BusinessesListingUI = (props: BusinessesListingParams) => {
322
320
  <BusinessFeaturedController
323
321
  key={bAry[1].id}
324
322
  business={bAry[1]}
323
+ isBusinessOpen={bAry[1]?.open}
325
324
  handleCustomClick={handleBusinessClick}
326
325
  orderType={orderState?.options?.type}
327
326
  />
@@ -361,6 +360,7 @@ const BusinessesListingUI = (props: BusinessesListingParams) => {
361
360
  <BusinessController
362
361
  key={business.id}
363
362
  business={business}
363
+ isBusinessOpen={business.open}
364
364
  handleCustomClick={handleBusinessClick}
365
365
  orderType={orderState?.options?.type}
366
366
  navigation={navigation}
@@ -103,6 +103,7 @@ const HighestRatedBusinessesUI = (props: HighestRatedBusinessesParams) => {
103
103
  >
104
104
  <BusinessController
105
105
  business={business}
106
+ isBusinessOpen={business?.open}
106
107
  handleCustomClick={onBusinessClick}
107
108
  orderType={orderState?.options?.type}
108
109
  navigation={navigation}
@@ -5,7 +5,7 @@ import { useTheme } from 'styled-components/native';
5
5
  import { OIcon, OText } from '../shared';
6
6
 
7
7
  const LogoutButtonUI = (props: any) => {
8
- const { handleLogoutClick, text, color } = props
8
+ const { handleLogoutClick, text, color, iconSize } = props
9
9
  const theme = useTheme();
10
10
 
11
11
  return (
@@ -15,7 +15,7 @@ const LogoutButtonUI = (props: any) => {
15
15
  >
16
16
  <OIcon
17
17
  src={theme.images.general.logout}
18
- width={17}
18
+ width={iconSize ?? 17}
19
19
  color={color ? color : theme.colors.textNormal}
20
20
  style={{ marginEnd: 14 }}
21
21
  />
@@ -20,19 +20,11 @@ import NavBar from '../NavBar';
20
20
  import { OButton, OIcon, OText } from '../shared';
21
21
  import { Container } from '../../layouts/Container';
22
22
  import {
23
- HeaderTitle,
24
23
  WrapSelectOption,
25
- Days,
26
- Day,
27
- WrapHours,
28
- Hours,
29
- Hour,
30
24
  WrapDelveryTime,
31
25
  } from './styles';
32
26
  import CalendarPicker from 'react-native-calendar-picker';
33
- import { TouchableRipple } from 'react-native-paper';
34
27
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
35
- import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome';
36
28
  import SelectDropdown from 'react-native-select-dropdown';
37
29
 
38
30
  const MomentOptionUI = (props: MomentOptionParams) => {
@@ -179,165 +171,165 @@ const MomentOptionUI = (props: MomentOptionParams) => {
179
171
 
180
172
  useEffect(() => {
181
173
  if (datesList?.length > 0) {
182
- const _datesList = datesList.slice(0, Number(configs?.max_days_preorder?.value || 6))
183
- const minDateParts = _datesList[0].split('-')
184
- const maxDateParts = _datesList[_datesList.length - 1].split('-')
185
- const _minDate = new Date(minDateParts[0], minDateParts[1] - 1, minDateParts[2])
186
- const _maxDate = new Date(maxDateParts[0], maxDateParts[1] - 1, maxDateParts[2])
187
- setMinDate(_minDate)
188
- setMaxDate(_maxDate)
174
+ const _datesList = datesList.slice(0, Number(configs?.max_days_preorder?.value || 6))
175
+ const minDateParts = _datesList[0].split('-')
176
+ const maxDateParts = _datesList[_datesList.length - 1].split('-')
177
+ const _minDate = new Date(minDateParts[0], minDateParts[1] - 1, minDateParts[2])
178
+ const _maxDate = new Date(maxDateParts[0], maxDateParts[1] - 1, maxDateParts[2])
179
+ setMinDate(_minDate)
180
+ setMaxDate(_maxDate)
189
181
  }
190
182
  }, [datesList])
191
183
 
192
184
  return (
193
185
  <>
194
- <Container style={{ paddingLeft: 40, paddingRight: 40 }}>
195
- <View style={{ paddingBottom: 90 }}>
196
- <NavBar
197
- onActionLeft={() => goToBack()}
198
- btnStyle={{ paddingLeft: 0 }}
199
- paddingTop={0}
200
- style={{ paddingBottom: 0, flexDirection: 'column', alignItems: 'flex-start' }}
201
- title={t('QUESTION_WHEN_ORDER', 'When do you want your order?')}
202
- titleAlign={'center'}
203
- titleStyle={{ fontSize: 14, marginRight: 0, marginLeft: 0 }}
204
- titleWrapStyle={{ paddingHorizontal: 0 }}
205
- />
186
+ <Container style={{ paddingLeft: 40, paddingRight: 40 }}>
187
+ <View style={{ paddingBottom: 90 }}>
188
+ <NavBar
189
+ onActionLeft={() => goToBack()}
190
+ btnStyle={{ paddingLeft: 0 }}
191
+ paddingTop={0}
192
+ style={{ paddingBottom: 0, flexDirection: 'column', alignItems: 'flex-start' }}
193
+ title={t('QUESTION_WHEN_ORDER', 'When do you want your order?')}
194
+ titleAlign={'center'}
195
+ titleStyle={{ fontSize: 14, marginRight: 0, marginLeft: 0 }}
196
+ titleWrapStyle={{ paddingHorizontal: 0 }}
197
+ />
206
198
 
207
- <WrapSelectOption
208
- onPress={() => _handleAsap()}
209
- disabled={orderState.loading} style={{ alignItems: 'flex-start' }}>
210
- {optionSelected.isAsap ? (
211
- <OIcon
212
- src={theme.images.general.option_checked}
213
- width={16}
214
- style={{ marginEnd: 24 }}
215
- />
216
- ) : (
217
- <OIcon
218
- src={theme.images.general.option_normal}
219
- width={16}
220
- style={{ marginEnd: 24 }}
221
- />
222
- )}
223
- <OText color={optionSelected.isAsap ? theme.colors.textNormal : theme.colors.disabled}>{t('ASAP_ABBREVIATION', 'ASAP') + ` (${moment().format('dddd, MMM d, yyyy h:mm A')} + delivery time)`}</OText>
224
- </WrapSelectOption>
225
- <WrapSelectOption
226
- onPress={() => setOptionSelected({ isAsap: false, isSchedule: true })}
227
- disabled={orderState.loading}>
228
- {optionSelected.isSchedule ? (
229
- <OIcon
230
- src={theme.images.general.option_checked}
231
- width={16}
232
- style={{ marginEnd: 24 }}
233
- />
234
- ) : (
235
- <OIcon
236
- src={theme.images.general.option_normal}
237
- width={16}
238
- style={{ marginEnd: 24 }}
239
- />
240
- )}
241
- <OText color={optionSelected.isSchedule ? theme.colors.textNormal : theme.colors.disabled}>{t('SCHEDULE_FOR_LATER', 'Schedule for later')}</OText>
242
- </WrapSelectOption>
199
+ <WrapSelectOption
200
+ onPress={() => _handleAsap()}
201
+ disabled={orderState.loading} style={{ alignItems: 'flex-start' }}>
202
+ {optionSelected.isAsap ? (
203
+ <OIcon
204
+ src={theme.images.general.option_checked}
205
+ width={16}
206
+ style={{ marginEnd: 24 }}
207
+ />
208
+ ) : (
209
+ <OIcon
210
+ src={theme.images.general.option_normal}
211
+ width={16}
212
+ style={{ marginEnd: 24 }}
213
+ />
214
+ )}
215
+ <OText color={optionSelected.isAsap ? theme.colors.textNormal : theme.colors.disabled}>{t('ASAP_ABBREVIATION', 'ASAP') + ` (${moment().format('dddd, MMM D, yyyy h:mm A')} + delivery time)`}</OText>
216
+ </WrapSelectOption>
217
+ <WrapSelectOption
218
+ onPress={() => setOptionSelected({ isAsap: false, isSchedule: true })}
219
+ disabled={orderState.loading}>
220
+ {optionSelected.isSchedule ? (
221
+ <OIcon
222
+ src={theme.images.general.option_checked}
223
+ width={16}
224
+ style={{ marginEnd: 24 }}
225
+ />
226
+ ) : (
227
+ <OIcon
228
+ src={theme.images.general.option_normal}
229
+ width={16}
230
+ style={{ marginEnd: 24 }}
231
+ />
232
+ )}
233
+ <OText color={optionSelected.isSchedule ? theme.colors.textNormal : theme.colors.disabled}>{t('SCHEDULE_FOR_LATER', 'Schedule for later')}</OText>
234
+ </WrapSelectOption>
243
235
 
244
- {optionSelected.isSchedule && (
245
- <WrapDelveryTime>
246
- {datesList.length > 0 && (
247
- <View style={styles.dateWrap}>
248
- <View style={styles.dateLabel}>
249
- <OText size={12} color={theme.colors.textNormal}>{dateSelected}</OText>
236
+ {optionSelected.isSchedule && (
237
+ <WrapDelveryTime>
238
+ {datesList.length > 0 && (
239
+ <View style={styles.dateWrap}>
240
+ <View style={styles.dateLabel}>
241
+ <OText size={12} color={theme.colors.textNormal}>{dateSelected}</OText>
242
+ </View>
243
+ <SelectDropdown
244
+ defaultButtonText={timeSelected ? timeSelected : t('DELIVERY_TIME', 'Delivery Time')}
245
+ defaultValue={74}
246
+ data={hoursList}
247
+ disabled={orderState.loading}
248
+ onSelect={(selectedItem, index) => {
249
+ setSelectedTime(selectedItem.startTime)
250
+ }}
251
+ buttonTextAfterSelection={(selectedItem, index) => {
252
+ return `${selectedItem.startTime} - ${selectedItem.endTime}`
253
+ }}
254
+ rowTextForSelection={(item, index) => {
255
+ return `${item.startTime} - ${item.endTime}`
256
+ }}
257
+ buttonStyle={{
258
+ backgroundColor: theme.colors.white,
259
+ borderColor: theme.colors.border,
260
+ borderWidth: 1,
261
+ borderRadius: 8,
262
+ height: 40,
263
+ width: '100%',
264
+ flexDirection: 'column',
265
+ alignItems: 'flex-start',
266
+ marginBottom: 20
267
+ }}
268
+ buttonTextStyle={{
269
+ color: theme.colors.textNormal,
270
+ fontSize: 12,
271
+ paddingTop: 10
272
+ }}
273
+ dropdownStyle={{
274
+ borderRadius: 8,
275
+ borderColor: theme.colors.lightGray,
276
+ }}
277
+ rowStyle={{
278
+ borderBottomColor: theme.colors.white,
279
+ backgroundColor: theme.colors.white,
280
+ height: 40,
281
+ flexDirection: 'column',
282
+ alignItems: 'flex-start',
283
+ paddingTop: 8,
284
+ paddingLeft: 22
285
+ }}
286
+ rowTextStyle={{
287
+ color: theme.colors.textNormal,
288
+ fontSize: 14,
289
+ }}
290
+ />
291
+ <CalendarPicker
292
+ nextTitle=">"
293
+ width={width - 80}
294
+ previousTitle="<"
295
+ nextComponent={
296
+ <OIcon
297
+ src={theme.images.general.chevron_right}
298
+ color={theme.colors.disabled}
299
+ width={12}
300
+ style={{ marginHorizontal: 4 }}
301
+ />
302
+ }
303
+ previousComponent={
304
+ <OIcon
305
+ src={theme.images.general.chevron_left}
306
+ color={theme.colors.disabled}
307
+ width={12}
308
+ style={{ marginHorizontal: 4 }}
309
+ />
310
+ }
311
+ onDateChange={(date: moment.Moment) =>
312
+ handleChangeDate(date.format('YYYY-MM-DD'))
313
+ }
314
+ selectedDayColor={theme.colors.primaryContrast}
315
+ todayBackgroundColor={theme.colors.border}
316
+ dayLabelsWrapper={{ borderColor: theme.colors.clear }}
317
+ customDayHeaderStyles={customDayHeaderStylesCallback}
318
+ weekdays={weekDays}
319
+ selectedStartDate={momento}
320
+ minDate={minDate}
321
+ maxDate={maxDate}
322
+ />
250
323
  </View>
251
- <SelectDropdown
252
- defaultButtonText={timeSelected ? timeSelected : t('DELIVERY_TIME', 'Delivery Time')}
253
- defaultValue={74}
254
- data={hoursList}
255
- disabled={orderState.loading}
256
- onSelect={(selectedItem, index) => {
257
- setSelectedTime(selectedItem.startTime)
258
- }}
259
- buttonTextAfterSelection={(selectedItem, index) => {
260
- return `${selectedItem.startTime} - ${selectedItem.endTime}`
261
- }}
262
- rowTextForSelection={(item, index) => {
263
- return `${item.startTime} - ${item.endTime}`
264
- }}
265
- buttonStyle={{
266
- backgroundColor: theme.colors.white,
267
- borderColor: theme.colors.border,
268
- borderWidth: 1,
269
- borderRadius: 8,
270
- height: 40,
271
- width: '100%',
272
- flexDirection: 'column',
273
- alignItems: 'flex-start',
274
- marginBottom: 20
275
- }}
276
- buttonTextStyle={{
277
- color: theme.colors.textNormal,
278
- fontSize: 12,
279
- paddingTop: 10
280
- }}
281
- dropdownStyle={{
282
- borderRadius: 8,
283
- borderColor: theme.colors.lightGray,
284
- }}
285
- rowStyle={{
286
- borderBottomColor: theme.colors.white,
287
- backgroundColor: theme.colors.white,
288
- height: 40,
289
- flexDirection: 'column',
290
- alignItems: 'flex-start',
291
- paddingTop: 8,
292
- paddingLeft: 22
293
- }}
294
- rowTextStyle={{
295
- color: theme.colors.textNormal,
296
- fontSize: 14,
297
- }}
298
- />
299
- <CalendarPicker
300
- nextTitle=">"
301
- width={width - 80}
302
- previousTitle="<"
303
- nextComponent={
304
- <OIcon
305
- src={theme.images.general.chevron_right}
306
- color={theme.colors.disabled}
307
- width={12}
308
- style={{ marginHorizontal: 4 }}
309
- />
310
- }
311
- previousComponent={
312
- <OIcon
313
- src={theme.images.general.chevron_left}
314
- color={theme.colors.disabled}
315
- width={12}
316
- style={{ marginHorizontal: 4 }}
317
- />
318
- }
319
- onDateChange={(date: moment.Moment) =>
320
- handleChangeDate(date.format('YYYY-MM-DD'))
321
- }
322
- selectedDayColor={theme.colors.primaryContrast}
323
- todayBackgroundColor={theme.colors.border}
324
- dayLabelsWrapper={{ borderColor: theme.colors.clear }}
325
- customDayHeaderStyles={customDayHeaderStylesCallback}
326
- weekdays={weekDays}
327
- selectedStartDate={momento}
328
- minDate={minDate}
329
- maxDate={maxDate}
330
- />
331
- </View>
332
- )}
333
- </WrapDelveryTime>
334
- )}
324
+ )}
325
+ </WrapDelveryTime>
326
+ )}
327
+ </View>
328
+ <Spinner visible={momentState.isLoading === 1} />
329
+ </Container>
330
+ <View style={{ position: 'absolute', bottom: bottom, paddingBottom: 20, paddingHorizontal: 40, backgroundColor: 'white', width: '100%' }}>
331
+ <OButton onClick={handleChangeMoment} isDisabled={!selectedTime} text={t('CONTINUE', 'Continue')} style={{ borderRadius: 7.6, height: 44, shadowOpacity: 0 }} textStyle={{ color: 'white', fontSize: 14 }} showNextIcon />
335
332
  </View>
336
- <Spinner visible={momentState.isLoading === 1} />
337
- </Container>
338
- <View style={{position: 'absolute', bottom: bottom, paddingBottom: 20, paddingHorizontal: 40, backgroundColor: 'white', width: '100%'}}>
339
- <OButton onClick={handleChangeMoment} isDisabled={!selectedTime} text={t('CONTINUE', 'Continue')} style={{borderRadius: 7.6, height: 44, shadowOpacity: 0}} textStyle={{color: 'white', fontSize: 14}} showNextIcon />
340
- </View>
341
333
  </>
342
334
  );
343
335
  };
@@ -155,7 +155,7 @@ export const ProductOptionsUI = (props: any) => {
155
155
  const { product, loading, error } = productObject;
156
156
  const [gallery, setGallery] = useState([])
157
157
  const [thumbsSwiper, setThumbsSwiper] = useState(0)
158
-
158
+ const [indexGallery, setIndexGallery] = useState(0)
159
159
  const [selOpt, setSelectedOpt] = useState(0);
160
160
  const [isHaveWeight, setIsHaveWeight] = useState(false)
161
161
  const [qtyBy, setQtyBy] = useState({
@@ -208,11 +208,16 @@ export const ProductOptionsUI = (props: any) => {
208
208
  }
209
209
 
210
210
  const handleClickThumb = (index: number) => {
211
- swiperRef?.current.scrollBy(index - thumbsSwiper, true);
211
+ if (index !== indexGallery) {
212
+ swiperRef?.current.scrollBy(index - thumbsSwiper, true);
213
+ setIndexGallery(index)
214
+ }
212
215
  }
213
216
 
214
217
  const handleRedirectLogin = () => {
215
- navigation.navigate('Login');
218
+ navigation.navigate('Login', {
219
+ store_slug: props.businessSlug
220
+ });
216
221
  };
217
222
 
218
223
  const handleSwitchQtyUnit = (val: string) => {
@@ -51,7 +51,6 @@ export const ProductOptionSubOptionUI = (props: any) => {
51
51
 
52
52
  const disableIncrement = option?.limit_suboptions_by_max ? balance === option?.max : state.quantity === suboption?.max || (!state.selected && balance === option?.max)
53
53
  const price = option?.with_half_option && suboption?.half_price && state.position !== 'whole' ? suboption?.half_price : suboption?.price
54
-
55
54
  return (
56
55
  <Container>
57
56
  <IconControl disabled={disabled} onPress={() => handleSuboptionClick()}>
@@ -73,54 +72,57 @@ export const ProductOptionSubOptionUI = (props: any) => {
73
72
  </OText>
74
73
  </IconControl>
75
74
  {showMessage && <OText size={10} mLeft={4} mRight={4} style={{ flex: 1, textAlign: 'center' }} color={theme.colors.primary}>{`${t('OPTIONS_MAX_LIMIT', 'Maximum options to choose')}: ${option?.max}`}</OText>}
76
- {option?.allow_suboption_quantity && state?.selected && (
77
- <QuantityControl>
78
- <Checkbox disabled={disabled || state.quantity === 0} onPress={decrement}>
79
- <OIcon
80
- src={theme.images.general.minus}
81
- width={16}
82
- color={state.quantity === 0 || disabled ? theme.colors.disabled : theme.colors.primary}
83
- />
84
- </Checkbox>
85
- <OText mLeft={5} mRight={5}>
86
- {state.quantity}
87
- </OText>
88
- <Checkbox disabled={disabled || disableIncrement} onPress={increment}>
89
- <OIcon
90
- src={theme.images.general.plus}
91
- width={16}
92
- color={disableIncrement || disabled ? theme.colors.disabled : theme.colors.primary}
93
- />
94
- </Checkbox>
95
- </QuantityControl>
96
- )}
97
- {option?.with_half_option && state?.selected && (
98
- <PositionControl>
99
- <Circle disabled={disabled} onPress={() => changePosition('left')}>
100
- <OIcon
101
- src={theme.images.general.half_l}
102
- color={state.selected && state.position === 'left' ? theme.colors.primary : '#cbcbcb'}
103
- width={16}
104
- style={styles.inverse}
105
- />
106
- </Circle>
107
- <Circle disabled={disabled} onPress={() => changePosition('whole')}>
108
- <OIcon
109
- src={theme.images.general.half_f}
110
- color={state.selected && state.position === 'whole' ? theme.colors.primary : '#cbcbcb'}
111
- width={16}
112
- />
113
- </Circle>
114
- <Circle disabled={disabled} onPress={() => changePosition('right')}>
115
- <OIcon
116
- src={theme.images.general.half_r}
117
- color={state.selected && state.position === 'right' ? theme.colors.primary : '#cbcbcb'}
118
- width={16}
119
- />
120
- </Circle>
121
- </PositionControl>
122
- )
123
- }
75
+ <QuantityControl>
76
+ {option?.allow_suboption_quantity && state?.selected && (
77
+ <>
78
+ <Checkbox disabled={disabled || state.quantity === 0} onPress={decrement}>
79
+ <OIcon
80
+ src={theme.images.general.minus}
81
+ width={16}
82
+ color={state.quantity === 0 || disabled ? theme.colors.disabled : theme.colors.primary}
83
+ />
84
+ </Checkbox>
85
+ <OText size={12} mLeft={5} mRight={5}>
86
+ {state.quantity}
87
+ </OText>
88
+ <Checkbox disabled={disabled || disableIncrement} onPress={increment}>
89
+ <OIcon
90
+ src={theme.images.general.plus}
91
+ width={16}
92
+ color={disableIncrement || disabled ? theme.colors.disabled : theme.colors.primary}
93
+ />
94
+ </Checkbox>
95
+ </>
96
+ )}
97
+ </QuantityControl>
98
+ <PositionControl>
99
+ {option?.with_half_option && state?.selected && (
100
+ <>
101
+ <Circle disabled={disabled} onPress={() => changePosition('left')}>
102
+ <OIcon
103
+ src={theme.images.general.half_l}
104
+ color={state.selected && state.position === 'left' ? theme.colors.primary : '#cbcbcb'}
105
+ width={16}
106
+ style={styles.inverse}
107
+ />
108
+ </Circle>
109
+ <Circle disabled={disabled} onPress={() => changePosition('whole')}>
110
+ <OIcon
111
+ src={theme.images.general.half_f}
112
+ color={state.selected && state.position === 'whole' ? theme.colors.primary : '#cbcbcb'}
113
+ width={16}
114
+ />
115
+ </Circle>
116
+ <Circle disabled={disabled} onPress={() => changePosition('right')}>
117
+ <OIcon
118
+ src={theme.images.general.half_r}
119
+ color={state.selected && state.position === 'right' ? theme.colors.primary : '#cbcbcb'}
120
+ width={16}
121
+ />
122
+ </Circle>
123
+ </>
124
+ )}
125
+ </PositionControl>
124
126
  <OText size={12} lineHeight={18} color={theme.colors.textSecondary}>
125
127
  + {parsePrice(price)}
126
128
  </OText>
@@ -5,28 +5,35 @@ export const Container = styled.View`
5
5
  align-items: center;
6
6
  justify-content: space-between;
7
7
  padding: 10px;
8
+ width: 100%;
8
9
  `
9
10
 
10
11
  export const IconControl = styled.TouchableOpacity`
11
12
  flex-direction: row;
12
- flex: 1;
13
+ width: 45%;
13
14
  align-items: center;
14
15
  `
15
16
 
16
17
  export const QuantityControl = styled.View`
17
18
  flex-direction: row;
18
19
  align-items: center;
20
+ justify-content: flex-start;
19
21
  margin-horizontal: 5px;
22
+ flex: 1;
23
+ width: 60px;
24
+
20
25
  `
21
26
 
22
27
  export const PositionControl = styled.View`
23
28
  flex-direction: row;
24
29
  align-items: center;
25
30
  margin-right: 5px;
31
+ flex: 1;
26
32
  `
27
33
 
28
34
  export const Checkbox = styled.TouchableOpacity`
29
35
  `
30
36
 
31
37
  export const Circle = styled.TouchableOpacity`
38
+ margin: 0 1px;
32
39
  `
@@ -37,19 +37,33 @@ const StripeElementsFormUI = (props: any) => {
37
37
  const { top, bottom } = useSafeAreaInsets();
38
38
  const [isKeyboardShow, setIsKeyboardShow] = useState(false);
39
39
 
40
- const billingDetails = {
41
- name: `${user.name} ${user.lastname}`,
42
- email: user.email,
43
- addressLine1: user.address
44
- };
40
+ let billingDetails: any = {}
41
+
42
+ if (user?.name || user?.lastname) {
43
+ if (user?.name) {
44
+ billingDetails.name = user?.name
45
+ }
46
+ if (user?.lastname) {
47
+ billingDetails.name = `${billingDetails?.name} ${user?.lastname}`
48
+ }
49
+ }
50
+
51
+ if (user?.email) {
52
+ billingDetails.email = user?.email
53
+ }
54
+
55
+ if (user?.address) {
56
+ billingDetails.addressLine1 = user?.address
57
+ }
45
58
 
46
59
  const createPayMethod = async () => {
60
+ const params: any = { type: 'Card' }
61
+ if (Object.keys(billingDetails).length > 0) {
62
+ params.billingDetails = billingDetails
63
+ }
47
64
  try {
48
65
  setCreatePmLoading(true)
49
- const { paymentMethod } = await createPaymentMethod({
50
- type: 'Card',
51
- billingDetails,
52
- });
66
+ const { paymentMethod } = await createPaymentMethod(params);
53
67
 
54
68
  setCreatePmLoading(false)
55
69
  handleSource && handleSource({
@@ -79,11 +93,12 @@ const StripeElementsFormUI = (props: any) => {
79
93
  createPayMethod();
80
94
  return
81
95
  }
96
+ const params: any = { type: 'Card' }
97
+ if (Object.keys(billingDetails).length > 0) {
98
+ params.billingDetails = billingDetails
99
+ }
82
100
  try {
83
- const { setupIntent, error } = await confirmSetupIntent(requirements, {
84
- type: 'Card',
85
- billingDetails,
86
- });
101
+ const { setupIntent, error } = await confirmSetupIntent(requirements, params);
87
102
 
88
103
  if (setupIntent?.status === 'Succeeded') {
89
104
  stripeTokenHandler(setupIntent?.paymentMethodId, user, businessId);
@@ -36,7 +36,7 @@ export const TaxInformation = (props: taxInformationParams) => {
36
36
  ? (product.tax?.id ? product.tax?.id === data?.id : product.tax?.id === null && data?.id === null)
37
37
  : type === 'fee'
38
38
  ? (product.fee?.id ? product.fee?.id === data?.id : (product.fee?.id === null && data?.id === null))
39
- : Object.keys(data?.discounts ?? {}).map(code => code.includes(product?.code))
39
+ : Object.keys(data?.discounts ?? {}).map(code => code.includes(product?.code)) && product?.offers?.find((offer : any) => offer?.name === data?.name)
40
40
  )
41
41
  }
42
42
 
@@ -0,0 +1,303 @@
1
+ import React, { useEffect, useState, useRef } from 'react';
2
+ import { useTheme } from 'styled-components/native';
3
+ import {
4
+ StyleSheet,
5
+ Text,
6
+ View,
7
+ TextInput,
8
+ SafeAreaView,
9
+ TouchableOpacity,
10
+ } from 'react-native';
11
+ import {
12
+ VerifyEmail as VerifyEmailController,
13
+ useToast,
14
+ useSession,
15
+ useLanguage,
16
+ ToastType
17
+ } from 'ordering-components/native';
18
+
19
+ import { OText, OInput, OButton } from '../shared';
20
+ import { LogoutButton } from '../LogoutButton'
21
+
22
+ import {
23
+ Container,
24
+ InputsSection,
25
+ WrapperText,
26
+ InputWrapper,
27
+ WrappCountdown,
28
+ CountDownContainer,
29
+ OtpSection,
30
+ DigitInput,
31
+ ButtonsActions,
32
+ WrapperActions
33
+ } from './styles'
34
+
35
+ const TIME_COUNTDOWN = 60 * 10 // 10 minutes
36
+ const CODE_LENGTH = 6;
37
+
38
+ const VerifyEmailUI = (props: any) => {
39
+ const {
40
+ verifyEmailState,
41
+ cleanErrorsState,
42
+ sendVerifyEmailCode,
43
+ checkVerifyEmailCode,
44
+ } = props
45
+
46
+ const theme = useTheme();
47
+ const [, t] = useLanguage()
48
+ const [{ user }] = useSession()
49
+ const [, { showToast }] = useToast();
50
+
51
+ const ref = useRef<TextInput>(null);
52
+
53
+ const [otpState, setOtpState] = useState('')
54
+ const [emailVerification, setEmailVerification] = useState(false)
55
+
56
+ const [timer, setTimer] = useState(`${TIME_COUNTDOWN / 60}:00`)
57
+ const [isSendCodeAgain, setIsSendCodeAgain] = useState(false)
58
+ const [containerIsFocused, setContainerIsFocused] = useState(false);
59
+
60
+ const codeDigitsArray = new Array(CODE_LENGTH).fill(0);
61
+
62
+ const style = StyleSheet.create({
63
+ inputContainer: {
64
+ borderWidth: 1,
65
+ borderRadius: 7.6,
66
+ padding: 12,
67
+ borderColor: theme.colors.disabled,
68
+ },
69
+ inputContainerFocused: {
70
+ borderColor: theme.colors.primary,
71
+ },
72
+ hiddenCodeInput: {
73
+ position: 'absolute',
74
+ height: 0,
75
+ width: 0,
76
+ opacity: 0,
77
+ },
78
+ inputStyle: {
79
+ marginBottom: 28,
80
+ borderWidth: 1,
81
+ borderColor: theme.colors.border,
82
+ borderRadius: 7.6,
83
+ },
84
+ btnStyle: {
85
+ borderRadius: 7.6,
86
+ marginTop: 5,
87
+ marginBottom: 2
88
+ }
89
+ });
90
+
91
+ const handleOnPress = () => {
92
+ setContainerIsFocused(true);
93
+ ref?.current?.focus();
94
+ };
95
+
96
+ const handleOnBlur = () => {
97
+ setContainerIsFocused(false);
98
+ };
99
+
100
+ const toDigitInput = (_value: number, idx: number) => {
101
+ const emptyInputChar = '0';
102
+ const digit = otpState[idx] || emptyInputChar;
103
+
104
+ const isCurrentDigit = idx === otpState.length;
105
+ const isLastDigit = idx === CODE_LENGTH - 1;
106
+ const isCodeFull = otpState.length === CODE_LENGTH;
107
+
108
+ const isFocused = isCurrentDigit || (isLastDigit && isCodeFull);
109
+
110
+ const containerStyle =
111
+ containerIsFocused && isFocused
112
+ ? {...style.inputContainer, ...style.inputContainerFocused}
113
+ : style.inputContainer;
114
+
115
+ return (
116
+ <View key={idx} style={containerStyle}>
117
+ <Text
118
+ style={{
119
+ fontSize: 20,
120
+ color: otpState[idx] ? theme.colors.black : theme.colors.disabled
121
+ }}
122
+ >
123
+ {digit}
124
+ </Text>
125
+ </View>
126
+ );
127
+ };
128
+
129
+ const handleSendOtp = () => {
130
+ setTimer(`${TIME_COUNTDOWN / 60}:00`)
131
+ setIsSendCodeAgain(true)
132
+ sendVerifyEmailCode({ email: user?.email })
133
+ }
134
+
135
+ useEffect(() => {
136
+ let _timer = TIME_COUNTDOWN - 1;
137
+ let minutes = 0;
138
+ let seconds = 0;
139
+ const interval = setInterval(() => {
140
+ minutes = _timer / 60;
141
+ seconds = _timer % 60;
142
+
143
+ minutes = minutes < 10 ? 0 + minutes : minutes;
144
+ seconds = seconds < 10 ? 0 + seconds : seconds;
145
+
146
+ const formatMinutes = parseInt(minutes.toString()) < 10
147
+ ? `0${parseInt(minutes.toString())}`
148
+ : parseInt(minutes.toString());
149
+
150
+ const formatseconds = parseInt(seconds.toString()) < 10
151
+ ? `0${parseInt(seconds.toString())}`
152
+ : parseInt(seconds.toString());
153
+
154
+ setTimer(`${formatMinutes}:${formatseconds}`);
155
+
156
+ if (--_timer < 0) {
157
+ clearInterval(interval);
158
+ }
159
+
160
+ if (timer === `${TIME_COUNTDOWN / 60}:00` && isSendCodeAgain) {
161
+ setIsSendCodeAgain(false)
162
+ clearInterval(interval);
163
+ }
164
+ }, 1000);
165
+
166
+ return () => clearInterval(interval)
167
+ }, [isSendCodeAgain])
168
+
169
+ useEffect(() => {
170
+ if (otpState?.length === CODE_LENGTH) {
171
+ if (emailVerification) {
172
+ checkVerifyEmailCode({ code: otpState })
173
+ return
174
+ }
175
+ }
176
+ }, [otpState])
177
+
178
+ useEffect(() => {
179
+ if (verifyEmailState?.errorSendCode || verifyEmailState?.errorCheckCode) {
180
+ showToast(
181
+ ToastType.Error,
182
+ verifyEmailState?.errorSendCode?.[0]
183
+ ?? verifyEmailState?.errorCheckCode?.[0]
184
+ ?? t('ERROR', 'Error'),
185
+ );
186
+ setTimeout(() => {
187
+ cleanErrorsState();
188
+ setOtpState('');
189
+ }, 2000);
190
+ }
191
+ }, [verifyEmailState])
192
+
193
+ useEffect(() => {
194
+ if (!verifyEmailState?.loadingSendCode) {
195
+ setEmailVerification(!!verifyEmailState?.resultSendCode)
196
+ }
197
+ }, [verifyEmailState])
198
+
199
+ return (
200
+ <SafeAreaView style={{ flex: 1 }}>
201
+ <Container>
202
+ <WrapperActions>
203
+ <WrapperText>
204
+ <OText size={22} weight='bold' style={{ marginBottom: 10 }}>
205
+ {t('VERIFICATION_CODE', 'Verification Code')}
206
+ </OText>
207
+ <OText size={14} color={theme.colors.disabled} style={{ textAlign: 'center', paddingVertical: 20 }}>
208
+ {!emailVerification ? (
209
+ t('VERIFICATION_CODE_MESSAGE', 'In order to continue using our platform please verify your email')
210
+ ) : (
211
+ t('VERIFICATION_CODE_SENT_MESSAGE', 'Please type the verification code sent to your email')
212
+ )}
213
+ </OText>
214
+ </WrapperText>
215
+ <View style={{ position: 'absolute', top: 0, right: 0 }}>
216
+ <LogoutButton iconSize={20} />
217
+ </View>
218
+ </WrapperActions>
219
+
220
+ {!emailVerification ? (
221
+ <InputWrapper>
222
+ <OInput
223
+ placeholder={user?.email}
224
+ style={style.inputStyle}
225
+ icon={theme.images.general.email}
226
+ isDisabled
227
+ />
228
+ </InputWrapper>
229
+ ) : (
230
+ <>
231
+ <WrappCountdown>
232
+ <CountDownContainer color={timer === '00:00' ? theme.colors.error: theme.colors.success}>
233
+ <OText
234
+ size={26}
235
+ color={timer === '00:00' ? theme.colors.error: theme.colors.success}
236
+ >
237
+ {timer}
238
+ </OText>
239
+ </CountDownContainer>
240
+ </WrappCountdown>
241
+
242
+ <InputsSection>
243
+ <OtpSection>
244
+ <DigitInput
245
+ disabled={otpState.length === CODE_LENGTH}
246
+ onPress={handleOnPress}
247
+ >
248
+ {codeDigitsArray.map(toDigitInput)}
249
+ </DigitInput>
250
+ <TextInput
251
+ ref={ref}
252
+ value={otpState}
253
+ placeholder='0'
254
+ onChangeText={setOtpState}
255
+ onSubmitEditing={handleOnBlur}
256
+ keyboardType="number-pad"
257
+ returnKeyType="done"
258
+ textContentType="oneTimeCode"
259
+ maxLength={CODE_LENGTH}
260
+ style={style.hiddenCodeInput}
261
+ />
262
+ </OtpSection>
263
+ </InputsSection>
264
+
265
+ <WrapperText>
266
+ <TouchableOpacity
267
+ onPress={handleSendOtp}
268
+ >
269
+ <OText color={theme.colors.primary}>
270
+ {t('RESEND_AGAIN', 'Resend again?')}
271
+ </OText>
272
+ </TouchableOpacity>
273
+ </WrapperText>
274
+ </>
275
+ )}
276
+ </Container>
277
+ <ButtonsActions>
278
+ <View style={{ width: '100%' }}>
279
+ <OButton
280
+ onClick={emailVerification ? () => setEmailVerification(false) : handleSendOtp}
281
+ text={emailVerification ? t('CANCEL', 'Cancel') : t('SEND_CODE', 'Send code')}
282
+ bgColor={emailVerification ? theme.colors.secundary : theme.colors.primary}
283
+ borderColor={emailVerification ? theme.colors.secundary : theme.colors.primary}
284
+ textStyle={{ color: emailVerification ? 'black' : 'white' }}
285
+ imgRightSrc={null}
286
+ isLoading={verifyEmailState?.loadingSendCode || verifyEmailState?.loadingCheckCode}
287
+ style={emailVerification ? style.btnStyle : { borderRadius: 7.6 }}
288
+ />
289
+ </View>
290
+ </ButtonsActions>
291
+ </SafeAreaView>
292
+ )
293
+ }
294
+
295
+ export const VerifyEmail = (props: any) => {
296
+ const verifyProps = {
297
+ ...props,
298
+ UIComponent: VerifyEmailUI
299
+ }
300
+ return (
301
+ <VerifyEmailController {...verifyProps} />
302
+ )
303
+ }
@@ -0,0 +1,77 @@
1
+ import styled from 'styled-components/native';
2
+
3
+ export const Container = styled.View`
4
+ padding: 20px;
5
+ `
6
+
7
+ export const WrapperText = styled.View`
8
+ display: flex;
9
+ flex-direction: column;
10
+ margin: 0 auto 0px;
11
+ align-items: center;
12
+ `
13
+
14
+ export const InputWrapper = styled.View``
15
+
16
+ export const WrapperActions = styled.View`
17
+ position: relative;
18
+ `
19
+
20
+ export const ButtonsActions = styled.View`
21
+ position: absolute;
22
+ bottom: 0px;
23
+ left: 0;
24
+ right: 0;
25
+ padding: 12px 40px;
26
+ flex-direction: row;
27
+ border-top-width: 1px;
28
+ border-color: ${(props: any) => props.theme.colors.border};
29
+ width: 100%;
30
+ justify-content: space-between;
31
+ background-color: #FFF;
32
+ z-index: 1000;
33
+ justify-content: space-between;
34
+ `
35
+
36
+ export const OtpSection = styled.SafeAreaView`
37
+ flex: 1;
38
+ align-items: center;
39
+ justify-content: center;
40
+ `
41
+
42
+ export const DigitInput = styled.Pressable`
43
+ width: 80%;
44
+ flex-direction: row;
45
+ justify-content: space-between;
46
+ `
47
+
48
+ export const CountDownContainer = styled.View`
49
+ background-color: ${(props: any) => `${props.color}4D`};
50
+ border-radius: 7.6px;
51
+ padding: 5px 0px 0px;
52
+ margin: 0 auto;
53
+ display: flex;
54
+ justify-content: center;
55
+ align-items: center;
56
+ width: 80%;
57
+ `
58
+
59
+ export const WrappCountdown = styled.View`
60
+ padding-bottom: 20px;
61
+ padding-top: 20px;
62
+ `
63
+
64
+ export const InputsSection = styled.View`
65
+ display: flex;
66
+ flex-direction: row;
67
+ justify-content: space-between;
68
+ padding-bottom: 20px;
69
+ `
70
+
71
+ export const ErrorSection = styled.View`
72
+ margin-bottom: 20px;
73
+ width: 100%;
74
+ display: flex;
75
+ justify-content: center;
76
+ flex-direction: column;
77
+ `