import React, { useState, useEffect, useCallback, useMemo } from 'react' import { useLanguage, useConfig, useUtils, useOrder, useValidationFields, useSession, useToast, ToastType, MultiCheckout as MultiCheckoutController } from 'ordering-components/native' import { View, StyleSheet, Platform, ScrollView, SafeAreaView } from 'react-native' import { useTheme } from 'styled-components/native'; import IconAntDesign from 'react-native-vector-icons/AntDesign'; import { Container } from '../../layouts/Container'; import NavBar from '../NavBar'; import { OText, OIcon, OModal, OButton } from '../shared'; import { getTypesText } from '../../utils'; import { UserDetails } from '../UserDetails' import { AddressDetails } from '../AddressDetails' import { MultiCart as MultiCartController } from '../MultiCart' import { MultiCartsPaymethodsAndWallets } from '../MultiCartsPaymethodsAndWallets' import { Cart } from '../Cart' import { FloatingButton } from '../FloatingButton' import { DriverTips } from '../DriverTips' import { CouponControl } from '../CouponControl'; import { DriverTipsContainer } from '../Cart/styles' import { OSTable, OSCoupon } from '../OrderSummary/styles'; import { SignupForm } from '../SignupForm' import { LoginForm } from '../LoginForm' import { TopHeader, TopActions, ChContainer, ChSection, ChHeader, CHMomentWrapper, ChUserDetails, ChAddress, ChCarts, CartsHeader, CCNotCarts, ChCartsTotal } from './styles' const mapConfigs = { mapZoom: 16, mapSize: { width: 640, height: 190 } } const MultiCheckoutUI = (props: any) => { const { navigation, placing, openCarts, handleGroupPlaceOrder, paymethodSelected, handleSelectPaymethod, handleSelectWallet, handlePaymethodDataChange, cartUuid, loyaltyPlansState, totalCartsFee, cartGroup, walletState, onNavigationRedirectReplace, merchantId, cartsInvalid, checkoutFieldsState } = props const theme = useTheme(); const styles = StyleSheet.create({ pagePadding: { paddingLeft: 20, paddingRight: 20 }, wrapperNavbar: { paddingHorizontal: 20, backgroundColor: theme?.colors?.white, borderWidth: 0 }, detailWrapper: { paddingHorizontal: 20, width: '100%' }, }) const [, { showToast }] = useToast(); const [, t] = useLanguage() const [{ configs }] = useConfig(); const [{ parsePrice, parseDate }] = useUtils(); const [{ options, carts, loading }, { confirmCart }] = useOrder(); const [validationFields] = useValidationFields(); const [{ user, loading: userLoading }, { login }] = useSession() const notFields = ['coupon', 'driver_tip', 'mobile_phone', 'address', 'zipcode', 'address_notes', 'comments'] const configTypes = configs?.order_types_allowed?.value.split('|').map((value: any) => Number(value)) || [] const isPreOrder = configs?.preorder_status_enabled?.value === '1' const isMultiDriverTips = configs?.checkout_multi_business_enabled?.value === '1' const allowDriverTipPickup = configs?.driver_tip_allowed_at_pickup?.value === '1' && options?.type === 2 const isGuestCheckoutEnabled = configs?.guest_checkout_enabled?.value === '1' const walletCarts = (Object.values(carts)?.filter((cart: any) => cart?.products && cart?.products?.length && cart?.status !== 2 && cart?.valid_schedule && cart?.valid_products && cart?.valid_address && cart?.valid_maximum && cart?.valid_minimum && cart?.wallets) || null) || [] const isChewLayout = theme?.header?.components?.layout?.type?.toLowerCase() === 'chew' const cartsToShow = openCarts?.length > 0 ? openCarts : cartsInvalid const walletName: any = { cash: { name: t('PAY_WITH_CASH_WALLET', 'Pay with Cash Wallet'), }, credit_point: { name: t('PAY_WITH_CREDITS_POINTS_WALLET', 'Pay with Credit Points Wallet'), } } const totalCartsPrice = cartGroup?.result?.balance const driverTipsOptions = typeof configs?.driver_tip_options?.value === 'string' ? JSON.parse(configs?.driver_tip_options?.value) || [] : configs?.driver_tip_options?.value || [] const creditPointGeneralPlan = loyaltyPlansState?.result?.find((loyal: any) => loyal.type === 'credit_point') const loyalBusinessAvailable = creditPointGeneralPlan?.businesses?.filter((b: any) => b.accumulates) ?? [] const checkoutFields = useMemo(() => checkoutFieldsState?.fields?.filter((field: any) => field.order_type_id === options?.type), [checkoutFieldsState, options]) const accumulationRateBusiness = (businessId: number) => { const value = loyalBusinessAvailable?.find((loyal: any) => loyal.business_id === businessId)?.accumulation_rate ?? 0 return value || (creditPointGeneralPlan?.accumulation_rate ?? 0) } const getIncludedTaxes = (cart: any) => { if (cart?.taxes === null || !cart?.taxes) { return cart.business.tax_type === 1 ? cart?.tax : 0 } else { return cart?.taxes.reduce((taxIncluded: number, tax: any) => { return taxIncluded + (tax.type === 1 ? tax.summary?.tax : 0) }, 0) } } const clearAmount = (value: any) => parseFloat((Math.trunc(value * 100) / 100).toFixed(configs.format_number_decimal_length?.value ?? 2)) const loyaltyRewardValue = openCarts ?.reduce((sum: any, cart: any) => sum + clearAmount((cart?.subtotal + getIncludedTaxes(cart)) * accumulationRateBusiness(cart?.business_id)), 0) ?.toFixed(configs.format_number_decimal_length?.value ?? 2) const [showTitle, setShowTitle] = useState(false) const [isUserDetailsEdit, setIsUserDetailsEdit] = useState(false); const [phoneUpdate, setPhoneUpdate] = useState(false); const [userErrors, setUserErrors] = useState([]); const [cartsOpened, setCartsOpened] = useState([]) const [placeByMethodPay, setPlaceByMethodPay] = useState(false) const [allowedGuest, setAllowedGuest] = useState(false) const [isOpen, setIsOpen] = useState(false) const [requiredFields, setRequiredFields] = useState([]) const stripePaymethods: any = ['stripe', 'stripe_direct', 'stripe_connect', 'stripe_redirect'] const [openModal, setOpenModal] = useState({ login: false, signup: false, isGuest: false }) const [methodPaySupported, setMethodPaySupported] = useState({ enabled: false, message: null, loading: true }) const methodsPay = ['global_google_pay', 'global_apple_pay'] const isDisablePlaceOrderButton = cartGroup?.loading || placing || (!(paymethodSelected?.paymethod_id || paymethodSelected?.wallet_id) && cartGroup?.result?.balance > 0) || (paymethodSelected?.paymethod?.gateway === 'stripe' && !paymethodSelected?.paymethod_data) || walletCarts.length > 0 || (methodsPay.includes(paymethodSelected?.gateway) && (!methodPaySupported.enabled || methodPaySupported.loading)) || openCarts?.length === 0 || (!isGuestCheckoutEnabled && !!user?.guest_id) const handleMomentClick = () => { if (isPreOrder) { navigation.navigate('MomentOption') } } const checkValidationFields = () => { setUserErrors([]) const errors: Array = [] const userSelected = user const _requiredFields: Array = [] Object.values(checkoutFieldsState?.fields).map((field: any) => { if (options?.type === field?.order_type_id && field?.enabled && field?.required && !notFields.includes(field?.validation_field?.code) ) { if (userSelected && !userSelected[field?.validation_field?.code]) { _requiredFields.push(field?.validation_field?.code) } } }) const mobilePhoneField: any = Object.values(checkoutFieldsState?.fields)?.find((field: any) => field?.order_type_id === options?.type && field?.validation_field?.code === 'mobile_phone') if ( userSelected && !userSelected?.cellphone && ((mobilePhoneField?.enabled && mobilePhoneField?.required) || configs?.verification_phone_required?.value === '1') ) { _requiredFields.push('cellphone') } setRequiredFields(_requiredFields) setUserErrors(errors) } const checkGuestValidationFields = () => { const userSelected = user const _requiredFields = checkoutFieldsState?.fields .filter((field) => (field?.order_type_id === options?.type) && field?.enabled && field?.required_with_guest && !notFields.includes(field?.validation_field?.code) && userSelected && !userSelected[field?.validation_field?.code]) const requiredFieldsCode = _requiredFields.map((item) => item?.validation_field?.code) const guestCheckoutCellPhone = checkoutFieldsState?.fields?.find((field) => field.order_type_id === options?.type && field?.validation_field?.code === 'mobile_phone') if ( userSelected && !userSelected?.cellphone && ((guestCheckoutCellPhone?.enabled && guestCheckoutCellPhone?.required_with_guest) || configs?.verification_phone_required?.value === '1') ) { requiredFieldsCode.push('cellphone') } setRequiredFields(requiredFieldsCode) } const togglePhoneUpdate = (val: boolean) => { setPhoneUpdate(val) } const handlePlaceOrder = (confirmPayment?: any) => { if (stripePaymethods.includes(paymethodSelected?.gateway) && user?.guest_id) { setOpenModal({ ...openModal, signup: true, isGuest: true }) return } if (!userErrors.length && !requiredFields?.length) { handleGroupPlaceOrder && handleGroupPlaceOrder(confirmPayment) return } if (requiredFields?.length) { setIsOpen(true) return } let stringError = '' Object.values(userErrors).map((item: any, i: number) => { stringError += (i + 1) === userErrors.length ? `- ${item?.message || item}` : `- ${item?.message || item}\n` }) showToast(ToastType.Error, stringError) setIsUserDetailsEdit(true) } const handlePlaceOrderAsGuest = () => { setIsOpen(false) handleGroupPlaceOrder && handleGroupPlaceOrder() } const handleSuccessSignup = (user: any) => { login({ user, token: user?.session?.access_token }) openModal?.isGuest && handlePlaceOrderAsGuest() setOpenModal({ ...openModal, signup: false, isGuest: false }) } const handleSuccessLogin = (user: any) => { if (user) setOpenModal({ ...openModal, login: false }) } const handleScroll = ({ nativeEvent: { contentOffset } }: any) => { setShowTitle(contentOffset.y > 30) } const handleGoBack = () => { if (navigation?.canGoBack()) { navigation.goBack() } else { navigation.navigate('BottomTab', { screen: 'Cart' }) } } useEffect(() => { if (checkoutFieldsState?.loading || userLoading) return if (user?.guest_id) { checkGuestValidationFields() } else { checkValidationFields() } }, [checkoutFieldsState, user, options?.type]) useEffect(() => { if (cartsToShow?.length === 1) { onNavigationRedirectReplace('CheckoutPage', { cartUuid: cartsToShow[0]?.uuid, fromMulti: true }) return } }, [cartsToShow]) useEffect(() => { if (walletState.error) { showToast(ToastType.Error, t(walletState.error, walletState.error?.[0]?.replace(/_/g, ' '))) } }, [walletState.error]) useEffect(() => { if (!cartUuid) { onNavigationRedirectReplace('BottomTab', { screen: 'Cart' }) } }, [cartUuid]) useEffect(() => { if (paymethodSelected?.gateway === 'global_google_pay') { setMethodPaySupported({ enabled: true, loading: false, message: null }) } }, [paymethodSelected]) const changeActiveState = useCallback((isClosed: boolean, uuid: string) => { const isActive = cartsOpened?.includes?.(uuid) if (isActive || !isClosed) { setCartsOpened(cartsOpened?.filter?.((_uuid) => _uuid !== uuid)) } else { setCartsOpened([ ...cartsOpened, uuid ]) } }, [cartsOpened]) return ( <> <> handleGoBack()}> {showTitle && ( {t('CHECKOUT', 'Checkout')} )} navigation?.canGoBack() && navigation.goBack()} showCall={false} paddingTop={Platform.OS === 'ios' ? 0 : 4} btnStyle={{ paddingLeft: 0 }} titleWrapStyle={{ paddingHorizontal: 0 }} titleStyle={{ marginRight: 0, marginLeft: 0 }} style={{ marginTop: 20 }} /> navigation.navigate('OrderTypes', { configTypes: configTypes })}> {t(getTypesText(options?.type || 1), 'Delivery')} handleMomentClick()} disabled={loading} > {options?.moment ? parseDate(options?.moment, { outputFormat: configs?.dates_moment_format?.value }) : t('ASAP_ABBREVIATION', 'ASAP')} {isPreOrder && ( )} {(user?.guest_id && !allowedGuest) ? ( {t('CUSTOMER_DETAILS', 'Customer details')} setOpenModal({ ...openModal, signup: true })} /> setOpenModal({ ...openModal, login: true })} /> {isGuestCheckoutEnabled && ( setAllowedGuest(true)} /> )} ) : ( )} {openCarts?.length > 0 && ( )} { isMultiDriverTips && (options?.type === 1 || allowDriverTipPickup) && validationFields?.fields?.checkout?.driver_tip?.enabled && openCarts.every((cart: any) => cart.business_id && cart.status !== 2) && driverTipsOptions && driverTipsOptions?.length > 0 && ( {t('DRIVER_TIPS', 'Driver Tips')} cart.business_id)} driverTipsOptions={driverTipsOptions} isFixedPrice={parseInt(configs?.driver_tip_type?.value, 10) === 1} isDriverTipUseCustom={!!parseInt(configs?.driver_tip_use_custom?.value, 10)} driverTip={parseInt(configs?.driver_tip_type?.value, 10) === 1 ? openCarts?.reduce((sum: any, cart: any) => sum + cart?.driver_tip, 0) : openCarts[0]?.driver_tip_rate} useOrderContext /> )} { validationFields?.fields?.checkout?.coupon?.enabled && openCarts.every((cart: any) => cart.business_id && cart.status !== 2) && configs?.multi_business_checkout_coupon_input_style?.value === 'group' && openCarts?.length > 0 && ( {t('DISCOUNT_COUPON', 'Discount coupon')} cart.business_id)} price={openCarts.reduce((total: any, cart: any) => total + cart.total, 0)} /> )} {t('MOBILE_FRONT_YOUR_ORDER', 'Your order')} {cartsToShow.map((cart: any) => ( props.navigation.navigate(route, params)} businessConfigs={cart?.business?.configs} cartsOpened={cartsOpened} changeActiveState={changeActiveState} isActive={cartsOpened?.includes?.(cart?.uuid)} /> {openCarts.length > 1 && ( )} ))} {!cartGroup?.loading && openCarts.length === 0 && cartsInvalid?.length === 0 && ( {t('CARTS_NOT_FOUND', 'You don\'t have carts available')} )} {walletCarts.length > 0 && ( {t('WARNING_PARTIAL_WALLET_CARTS', 'Important: One or more carts can`t be completed due a partial payment with cash/points wallet and requires to be paid individually')} )} {openCarts.length > 1 && ( {!!totalCartsFee && configs?.multi_business_checkout_show_combined_delivery_fee?.value === '1' && ( {t('TOTAL_DELIVERY_FEE', 'Total delivery fee')} {parsePrice(totalCartsFee)} )} {openCarts.reduce((sum: any, cart: any) => sum + cart?.driver_tip, 0) > 0 && configs?.multi_business_checkout_show_combined_driver_tip?.value === '1' && ( {t('DRIVER_TIP', 'Driver tip')} {parsePrice(openCarts.reduce((sum: any, cart: any) => sum + cart?.driver_tip, 0))} )} {!cartGroup?.loading && cartGroup?.result?.payment_events?.length > 0 && cartGroup?.result?.payment_events?.map((event: any) => ( {walletName[cartGroup?.result?.wallets?.find((wallet: any) => wallet.wallet_id === event.wallet_id)?.type]?.name} -{parsePrice(event.amount, { isTruncable: true })} ))} {t('TOTAL_FOR_ALL_CARTS', 'Total for all Carts')} {parsePrice(totalCartsPrice)} {!!loyaltyRewardValue && ( {t('REWARD_LOYALTY_POINT', 'Reward :amount: on loyalty points').replace(':amount:', loyaltyRewardValue)} )} {t('MULTI_CHECKOUT_DESCRIPTION', 'You will receive a receipt for each business. The payment is not combined between multiple stores. Each payment is processed by the store')} )} {cartsToShow?.some((cart: any) => !cart?.valid_products && cart?.status !== 2) && ( {t('WARNING_INVALID_PRODUCTS_CHECKOUT', 'To continue with your checkout, please remove from your cart the products that are not available.')} )} {(!isGuestCheckoutEnabled && !!user?.guest_id) && ( {t('LOGIN_SIGN_UP_COMPLETE_ORDER', 'Login/Sign up to complete your order.')} )} setOpenModal({ ...openModal, signup: false, isGuest: false })} > setOpenModal({ ...openModal, login: false })} > setIsOpen(false)} > { setIsOpen(false) handlePlaceOrder() }} setIsOpen={setIsOpen} /> setPlaceByMethodPay(true) : () => handlePlaceOrder()} isSecondaryBtn={isDisablePlaceOrderButton} disabled={isDisablePlaceOrderButton} btnText={placing ? t('PLACING', 'Placing') : t('PLACE_ORDER', 'Place Order')} btnRightValueShow btnRightValue={parsePrice(totalCartsPrice)} iosBottom={30} /> ) } export const MultiCheckout = (props: any) => { const [loadMultiCarts, setLoadMultiCarts] = useState(!!props.route?.params?.checkCarts) const [cartUuid, setCartUuid] = useState('') const multiCheckoutProps = { ...props, cartUuid: props.route?.params?.cartUuid ?? cartUuid, UIComponent: MultiCheckoutUI } const multiCartProps = { ...props, handleOnRedirectMultiCheckout: (cartUuid: string) => { setCartUuid(cartUuid) setLoadMultiCarts(false) }, handleOnRedirectCheckout: (cartUuid: string) => { props.navigation.navigate('CheckoutNavigator', { screen: 'CheckoutPage', cartUuid }) } } return ( loadMultiCarts ? ( ) : ( ) ) }