import React, { useEffect, useState } from 'react' import { View, StyleSheet, BackHandler, Alert, Keyboard } from 'react-native' import Spinner from 'react-native-loading-spinner-overlay' import { useForm, Controller } from 'react-hook-form' import { useLanguage, OrderDetails as OrderDetailsConTableoller, useUtils, useApi, useSession, useToast, ToastType, useConfig } from 'ordering-components/native' import { PhoneInputNumber } from '../PhoneInputNumber' import { OSOrderDetailsWrapper, OSTable, OSActions, OSInputWrapper, SentReceipt, Table, OrderBill, Total, OSRow, } from './styles' import { OrderDetailsParams, Product } from '../../types' import { Container } from '../../layouts/Container'; import NavBar from '../../components/NavBar'; import { OButton, OIconButton, OImage, OInput, OText } from '../../components/shared'; import GridContainer from '../../layouts/GridContainer'; import OptionSwitch, { Opt } from '../../components/shared/OOptionToggle'; import { verifyDecimals } from '../../../../../src/utils' import { LANDSCAPE, PORTRAIT, useDeviceOrientation } from '../../../../../src/hooks/DeviceOrientation' import { useTheme } from 'styled-components/native' import { _retrieveStoreData, _setStoreData } from '../../../../../src/providers/StoreUtil'; import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons' import EvilIcons from 'react-native-vector-icons/EvilIcons' const _EMAIL = 'email'; const _SMS = 'sms'; export const OrderDetailsUI = (props: OrderDetailsParams) => { const { navigation, isFromCheckout } = props; const theme = useTheme(); const [, t] = useLanguage() const [ordering] = useApi() const [, { showToast }] = useToast() const [{ parsePrice, parseNumber }] = useUtils() const [orientationState] = useDeviceOrientation() const [{ token }] = useSession() const [{ configs }] = useConfig() const { control, handleSubmit, errors } = useForm(); const [customerName, setCustomerName] = useState(null) const [countReceipts, setCountReceipts] = useState(5) const [isLoading, setIsLoading] = useState(false) const [phoneInputData, setPhoneInputData] = useState({ error: '', phone: { country_phone_code: null, cellphone: null, }, }); const { order } = props.order; const isTaxIncluded = order?.tax_type === 1 const styles = StyleSheet.create({ inputsStyle: { borderColor: theme.colors.disabled, borderBottomRightRadius: 0, borderBottomLeftRadius: 8, borderTopRightRadius: 0, borderTopLeftRadius: 8, flex: 1, height: 52 }, buttonApplyStyle: { borderBottomRightRadius: 8, borderBottomLeftRadius: 0, borderTopRightRadius: 8, borderTopLeftRadius: 0, }, textButtonApplyStyle: { color: theme.colors.primary, marginLeft: 0, marginRight: 0 }, disabledTextButtonApplyStyle: { color: theme.colors.white, marginLeft: 0, marginRight: 0 }, textBold: { fontWeight: 'bold' }, }); const optionsToSendReceipt: Opt[] = [ { key: _EMAIL, label: t('EMAIL', 'Email'), value: _EMAIL, isDefault: true, }, { key: _SMS, label: t('SMS', 'SMS'), value: _SMS, }, ]; const [optionToSendReceipt, setOptionToSendReceipt] = useState( optionsToSendReceipt?.find(o => o?.isDefault), ); const handleArrowBack: any = () => { if (!isFromCheckout) { navigation?.canGoBack() && navigation.goBack(); return } navigation.navigate('BottomTab'); } useEffect(() => { BackHandler.addEventListener('hardwareBackPress', handleArrowBack); return () => { BackHandler.removeEventListener('hardwareBackPress', handleArrowBack); } }, []) const handleChangeInputEmail = (value: string, onChange: any) => { onChange(value.toLowerCase().replace(/[&,()%";:รง?<>{}\\[\]\s]/g, '')); }; const onSubmit = async (values: any) => { Keyboard.dismiss(); if (countReceipts <= 0) { showToast(ToastType.Error, t('MAXIMUM_RECEIPTS_SEND_EXCEEDED', 'The maximum receipts sent has been exceeded')) return } if (phoneInputData.error) { showToast(ToastType.Error, phoneInputData.error); return; } setIsLoading(true) let body if (optionToSendReceipt?.value === _EMAIL) { body = { channel: 1, email: values.email, name: customerName } } else if (optionToSendReceipt?.value === _SMS) { body = { channel: 2, cellphone: phoneInputData?.phone?.cellphone, country_phone_code: phoneInputData?.phone?.country_phone_code, name: customerName } } try { const response = await fetch(`${ordering.root}/orders/${order.id}/receipt`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: JSON.stringify(body), }) const { error, result } = await response.json() if (error) { showToast(ToastType.Error, result) } else { showToast(ToastType.Success, t('RECEIPT_SEND_SUCCESSFULLY', 'Receipt send successfully')) setCountReceipts(countReceipts - 1) } } catch (error: any) { showToast(ToastType.Error, error.message) } setIsLoading(false) } const getIncludedTaxes = () => { if (order?.taxes?.length === 0) { return order.tax_type === 1 ? order?.summary?.tax ?? 0 : 0 } else { return order?.taxes.reduce((taxIncluded: number, tax: any) => { return taxIncluded + (tax.type === 1 ? tax.summary?.tax : 0) }, 0) } } const getIncludedTaxesDiscounts = () => { return order?.taxes?.filter((tax: any) => tax?.type === 1)?.reduce((carry: number, tax: any) => carry + (tax?.summary?.tax_after_discount ?? tax?.summary?.tax), 0) } useEffect(() => { const backAction = () => { Alert.alert(`${t('HOLD_ON', 'Hold on')}!`, `${t('ARE_YOU_SURE_YOU_WANT_TO_GO_BACK', 'Are you sure you want to go back')}?`, [ { text: t('CANCEL', 'cancel'), onPress: () => null, style: "cancel" }, { text: t('YES', 'yes'), onPress: () => { navigation.reset({ routes: [{ name: 'Intro' }] }); } } ]); return true; }; const backHandler = BackHandler.addEventListener( "hardwareBackPress", backAction ); return () => backHandler.remove(); }, []); useEffect(() => { const getCustomerName = async () => { try { const { customerName: name } = await _retrieveStoreData('customer_name') setCustomerName(name) } catch (e) { if (e) { setCustomerName(null) } } } getCustomerName() const redirectHome = setTimeout(() => { _setStoreData('customer_name', { customerName: '' }); navigation.reset({ routes: [{ name: 'Intro' }], }); }, 60000); return () => { clearTimeout(redirectHome); } }, []) useEffect(() => { if (Object.keys(errors).length > 0) { // Convert all errors in one string to show in toast provider const list = Object.values(errors); let stringError = ''; if (phoneInputData.error) { list.unshift({ message: phoneInputData.error }); } if ( optionToSendReceipt?.value === _SMS && !phoneInputData.error && !phoneInputData.phone.country_phone_code && !phoneInputData.phone.cellphone ) { list.unshift({ message: t( 'VALIDATION_ERROR_MOBILE_PHONE_REQUIRED', 'The field Mobile phone is required.', ), }); } list.map((item: any, i: number) => { stringError += i + 1 === list.length ? `- ${item.message}` : `- ${item.message}\n`; }); showToast(ToastType.Error, stringError); } }, [errors]); const actionsContent = ( {t('SEND_RECEIPT', 'Send receipt')} {countReceipts}/5 {t('RECIPTS_REMAINING', 'Recipts remaining')} {/* */} {optionToSendReceipt?.value === _EMAIL && ( ( handleChangeInputEmail(e, onChange)} style={styles.inputsStyle} value={value} autoCapitalize="none" autoCorrect={false} returnKeyType="done" onSubmitEditing={handleSubmit(onSubmit)} blurOnSubmit type="email-address" /> )} name="email" rules={{ required: t( 'VALIDATION_ERROR_EMAIL_REQUIRED', 'The field Email is required', ).replace('_attribute_', t('EMAIL', 'Email')), pattern: { value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, message: t( 'INVALID_ERROR_EMAIL', 'Invalid email address', ).replace('_attribute_', t('EMAIL', 'Email')), }, }} defaultValue="" /> )} {optionToSendReceipt?.value === _SMS && ( setPhoneInputData(val)} textInputProps={{ returnKeyType: 'done', onSubmitEditing: handleSubmit(onSubmit), }} /> )} { _setStoreData('customer_name', { customerName: '' }); navigation.reset({ routes: [{ name: 'Intro' }], }); }} /> ); const orderDetailsContent = ( {t('ORDER_NUMBER', 'Order No.')} {' '} {order?.id} {order?.products?.length > 0 && ( <> {`${order?.products?.length} ${t('ITEMS', 'items')}`} {parsePrice((order?.summary?.total || order?.total) - (order?.summary?.discount || order?.discount))} {order?.products.map((product: Product, i: number) => ( ))} )} {t('SUBTOTAL', 'Subtotal')} {parsePrice(((order?.summary?.subtotal ?? order?.subtotal) + getIncludedTaxes()))}
{(order?.summary?.discount > 0 ?? order?.discount > 0) && order?.offers?.length === 0 && ( {order?.offer_type === 1 ? ( {t('DISCOUNT', 'Discount')} {`(${verifyDecimals( order?.offer_rate, parsePrice, )}%)`} ) : ( {t('DISCOUNT', 'Discount')} )} - {parsePrice(order?.summary?.discount || order?.discount)}
)} { order?.offers?.length > 0 && order?.offers?.filter((offer: any) => offer?.target === 1)?.map((offer: any) => ( {offer.name} {offer.rate_type === 1 && ( {`(${verifyDecimals(offer?.rate, parsePrice)}%)`} )} - {parsePrice(offer?.summary?.discount)}
)) } {order?.summary?.subtotal_with_discount > 0 && order?.summary?.discount > 0 && order?.summary?.total >= 0 && ( {t('SUBTOTAL_WITH_DISCOUNT', 'Subtotal with discount')} {order?.tax_type === 1 ? ( {parsePrice((order?.summary?.subtotal_with_discount + getIncludedTaxesDiscounts() ?? 0))} ) : ( {parsePrice(order?.summary?.subtotal_with_discount ?? 0)} )}
)} { order?.taxes?.length === 0 && order?.tax_type === 2 && ( {t('TAX', 'Tax')} {`(${verifyDecimals(order?.tax, parseNumber)}%)`} {parsePrice(order?.summary?.tax || 0)}
) } { order?.fees?.length === 0 && ( {t('SERVICE_FEE', 'Service fee')} {`(${verifyDecimals(order?.service_fee, parseNumber)}%)`} {parsePrice(order?.summary?.service_fee || 0)}
) } { order?.taxes?.length > 0 && order?.taxes?.filter((tax: any) => tax?.type === 2 && tax?.rate !== 0).map((tax: any) => ( {tax.name || t('INHERIT_FROM_BUSINESS', 'Inherit from business')} {`(${verifyDecimals(tax?.rate, parseNumber)}%)`}{' '} {parsePrice(tax?.summary?.tax_after_discount ?? tax?.summary?.tax ?? 0)}
)) } { order?.fees?.length > 0 && order?.fees?.filter((fee: any) => !(fee.fixed === 0 && fee.percentage === 0))?.map((fee: any) => ( {fee.name || t('INHERIT_FROM_BUSINESS', 'Inherit from business')} ({fee?.fixed > 0 && `${parsePrice(fee?.fixed)}${fee.percentage > 0 ? ' + ' : ''}`}{fee.percentage > 0 && `${fee.percentage}%`}){' '} {parsePrice(fee?.summary?.fixed + (fee?.summary?.percentage_after_discount ?? fee?.summary?.percentage) ?? 0)}
)) } { order?.offers?.length > 0 && order?.offers?.filter((offer: any) => offer?.target === 3)?.map((offer: any) => ( {offer.name} {offer.rate_type === 1 && ( {`(${verifyDecimals(offer?.rate, parsePrice)}%)`} )} - {parsePrice(offer?.summary?.discount)}
)) } {order?.summary?.delivery_price > 0 && ( {t('DELIVERY_FEE', 'Delivery Fee')}{parsePrice(order?.summary?.delivery_price)}
)} { order?.offers?.length > 0 && order?.offers?.filter((offer: any) => offer?.target === 2)?.map((offer: any) => ( {offer.name} {offer.rate_type === 1 && ( {`(${verifyDecimals(offer?.rate, parsePrice)}%)`} )} - {parsePrice(offer?.summary?.discount)}
)) } {order?.summary?.driver_tip > 0 && ( {t('DRIVER_TIP', 'Driver tip')} {order?.summary?.driver_tip > 0 && parseInt(configs?.driver_tip_type?.value, 10) === 2 && !parseInt(configs?.driver_tip_use_custom?.value, 10) && ( `(${verifyDecimals(order?.summary?.driver_tip, parseNumber)}%)` )} {parsePrice(order?.summary?.driver_tip ?? order?.totalDriverTip)}
)} {t('TOTAL', 'Total')} {parsePrice(order?.summary?.total ?? order?.total)}
); return ( <> {!!order && Object.keys(order).length > 0 && ( <> } style={{ flex: 1, justifyContent: 'flex-end', left: 30 }} onClick={() => { navigation.reset({ routes: [{ name: 'Intro' }], }); }} /> } /> {orderDetailsContent} {t('WE_KNOW_YOU_ARE', 'We know you are')} {'\n'} {t('HUNGRY', 'hungry')}{!!customerName && `, ${customerName}`} {t('TO_FINISH_TAKE_YOUR_RECEIPT_AND_GO_TO_THE_FRONT_COUNTER', "We`ve received your order and we will call you by your name or your order number.")} {orientationState?.orientation === LANDSCAPE && actionsContent} {orientationState?.orientation === PORTRAIT && ( {actionsContent} )} )} ) } export const OrderDetails = (props: OrderDetailsParams) => { const orderDetailsProps = { ...props, UIComponent: OrderDetailsUI } return ( ) }