Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion consumer/v2/food/home/FoodOrderHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useSearch } from '../../../../common/store/api/search/useSearch';
import { useSegmentScreen } from '../../../../common/store/api/track';
import {
updateCurrentLocation,
updateCurrentPlace,
updateCurrentPlace
} from '../../../../common/store/consumer/actions';
import { getConsumer, getCurrentLocation } from '../../../../common/store/consumer/selectors';
import { SearchFilter } from '../../../../common/store/consumer/types';
Expand Down Expand Up @@ -50,6 +50,10 @@ export const FoodOrderHome = ({ route, navigation }: Props) => {
refetch,
fetchNextPage,
} = useSearch<BusinessAlgolia>(true, 'restaurant', 'distance', filters, currentLocation, '');
if (restaurants) {
restaurants[0].status = 'open';
}

const [refreshing, setRefreshing] = React.useState(false);
const mostRecentRestaurants = useLastRestaurants(consumer?.id);
// side effects
Expand Down
67 changes: 52 additions & 15 deletions consumer/v2/food/restaurant/product/ItemDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
ComplementGroup,
OrderItem,
OrderItemComplement,
WithId,
WithId
} from '@appjusto/types';
import { Feather } from '@expo/vector-icons';
import { CompositeNavigationProp, RouteProp } from '@react-navigation/native';
Expand All @@ -19,25 +19,22 @@ import PaddedView from '../../../../../common/components/containers/PaddedView';
import DefaultInput from '../../../../../common/components/inputs/DefaultInput';
import HR from '../../../../../common/components/views/HR';
import { IconSemaphoreSmall } from '../../../../../common/icons/icon-semaphore-small';
import useLastKnownLocation from '../../../../../common/location/useLastKnownLocation';
import { UnloggedParamList } from '../../../../../common/screens/unlogged/types';
import { useProduct } from '../../../../../common/store/api/business/hooks/useProduct';
import { useProductImageURI } from '../../../../../common/store/api/business/hooks/useProductImageURI';
import { getNextAvailableDate } from '../../../../../common/store/api/business/selectors';
import { distanceBetweenLatLng } from '../../../../../common/store/api/helpers';
import * as helpers from '../../../../../common/store/api/order/helpers';
import { track, useSegmentScreen } from '../../../../../common/store/api/track';
import {
getConsumer,
getCurrentLocation,
getCurrentPlace,
} from '../../../../../common/store/consumer/selectors';
import { getConsumer, getCurrentPlace } from '../../../../../common/store/consumer/selectors';
import {
useContextBusiness,
useContextBusinessId,
useContextBusinessId
} from '../../../../../common/store/context/business';
import {
useContextGetComplementGroup,
useContextGetProductCategory,
useContextGetProductCategory
} from '../../../../../common/store/context/menu';
import { useContextActiveOrder } from '../../../../../common/store/context/order';
import {
Expand All @@ -46,7 +43,7 @@ import {
halfPadding,
padding,
screens,
texts,
texts
} from '../../../../../common/styles';
import { formatCurrency, formatHour } from '../../../../../common/utils/formatters';
import { t } from '../../../../../strings';
Expand All @@ -56,6 +53,7 @@ import { RestaurantNavigatorParamList } from '../types';
import { useBusinessIsAcceptingOrders } from '../useBusinessIsAcceptingOrders';
import { ItemComplements } from './ItemComplements';
import { ItemQuantity } from './ItemQuantity';
import { LocationDistantFromDestinationModal } from './LocationDistantFromDestinationModal';

type ScreenNavigationProp = CompositeNavigationProp<
StackNavigationProp<RestaurantNavigatorParamList, 'ItemDetail'>,
Expand Down Expand Up @@ -85,12 +83,18 @@ export const ItemDetail = ({ navigation, route }: Props) => {
const consumer = useSelector(getConsumer);
const currentPlace = useSelector(getCurrentPlace);
// state
const location = useSelector(getCurrentLocation);
const destination = activeOrder?.destination?.location ?? currentPlace?.location ?? location;

const { coords } = useLastKnownLocation();

const destination = activeOrder?.destination?.location;
const location = coords ?? currentPlace?.location;
const distance =
destination && business?.businessAddress?.latlng
? distanceBetweenLatLng(destination, business.businessAddress.latlng)
: 0;
const distanceBetweenLocationAndDestinationMeters =
location && destination ? distanceBetweenLatLng(location, destination) : 0;
const bottomLimitWarningModalShowMeters = 2000;
const isOutOfRange = (business?.deliveryRange ?? 0) < (distance ?? 0);
const isAcceptingOrders = useBusinessIsAcceptingOrders(business);
const product = useProduct(businessId, productId);
Expand All @@ -99,6 +103,7 @@ export const ItemDetail = ({ navigation, route }: Props) => {
const [quantity, setQuantity] = React.useState(1);
const [complements, setComplements] = React.useState<OrderItemComplement[]>([]);
const [notes, setNotes] = React.useState<string>('');
const [modalVisible, setModalVisible] = React.useState(false);
const orderItem = React.useMemo(() => {
if (!product) return undefined;
return {
Expand All @@ -114,6 +119,14 @@ export const ItemDetail = ({ navigation, route }: Props) => {
complements,
} as OrderItem;
}, [product, itemId, quantity, notes, complements, getProductCategory]);

const isLocationDistantFromDestination = React.useMemo(
(): boolean =>
(activeOrder?.items?.length ?? 0) === 0 &&
distanceBetweenLocationAndDestinationMeters > bottomLimitWarningModalShowMeters,
[activeOrder, distanceBetweenLocationAndDestinationMeters, bottomLimitWarningModalShowMeters]
);

const canAddItemToOrder = React.useMemo(() => {
if (!product) return false;
return helpers.hasSatisfiedAllGroups(product, complements);
Expand All @@ -132,10 +145,16 @@ export const ItemDetail = ({ navigation, route }: Props) => {
if (!product) return;
const item = activeOrder.items?.find((i) => i.id === itemId);
if (!item) return;

setComplements(item.complements ?? []);
setQuantity(item.quantity);
setNotes(item.notes ?? '');
}, [itemId, activeOrder, product]);
React.useEffect(() => {
if (!activeOrder) {
api.order().createFoodOrder(business!, consumer!, [], currentPlace ?? null);
}
}, []);
// tracking
useSegmentScreen('ItemDetail');
// UI
Expand Down Expand Up @@ -181,22 +200,34 @@ export const ItemDetail = ({ navigation, route }: Props) => {
}
};
// handlers
const handleAddItemToOrder = () => {
Keyboard.dismiss();
if (isLocationDistantFromDestination) {
setModalVisible(true);
} else {
updateOrder();
}
};

const updateOrder = () => {
(async () => {
Keyboard.dismiss();
if (!orderItem) return;

console.log(activeOrder?.items);

if (!activeOrder) {
api.order().createFoodOrder(business, consumer!, [orderItem], currentPlace ?? null);
track('consumer created food order in database');
} else {
const updatedOrder = !itemId
? helpers.addItemToOrder(activeOrder, orderItem)
: quantity > 0
? helpers.updateItem(activeOrder, orderItem)
: helpers.removeItem(activeOrder, orderItem);
? helpers.updateItem(activeOrder, orderItem)
: helpers.removeItem(activeOrder, orderItem);
api.order().updateOrder(activeOrder.id, updatedOrder);
track('consumer updated items in order');
}

navigation.pop();
})();
};
Expand Down Expand Up @@ -357,11 +388,17 @@ export const ItemDetail = ({ navigation, route }: Props) => {
title={`${t('Adicionar')} ${formatCurrency(helpers.getItemTotal(orderItem!))}`}
disabled={!canAddItemToOrder}
onChange={(value) => setQuantity(value)}
onSubmit={updateOrder}
onSubmit={handleAddItemToOrder}
/>
</PaddedView>
</View>
) : null}
<LocationDistantFromDestinationModal
modalVisible={modalVisible}
onModalClose={() => setModalVisible(false)}
positiveAnswer={updateOrder}
negativeAnswer={() => setModalVisible(false)}
/>
</View>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { MaterialIcons } from '@expo/vector-icons';
import React from 'react';
import { Modal, Text, TouchableOpacity, View } from 'react-native';
import DefaultButton from '../../../../../common/components/buttons/DefaultButton';
import PaddedView from '../../../../../common/components/containers/PaddedView';
import { borders, colors, halfPadding, padding, texts } from '../../../../../common/styles';
import { t } from '../../../../../strings';

type Props = {
modalVisible: boolean;
onModalClose: () => void;
positiveAnswer: () => void;
negativeAnswer: () => void;
};

export const LocationDistantFromDestinationModal = ({
modalVisible,
onModalClose,
positiveAnswer,
negativeAnswer,
}: Props) => {
return (
<Modal transparent animationType="slide" visible={modalVisible}>
<View style={{ flex: 1, justifyContent: 'flex-end', backgroundColor: 'rgba(0, 0, 0, 0.8)' }}>
<PaddedView style={{ backgroundColor: colors.white, ...borders.default }}>
<View style={{ alignItems: 'flex-end' }}>
<TouchableOpacity onPress={onModalClose}>
<View
style={{
width: 32,
height: 32,
justifyContent: 'center',
alignItems: 'center',
}}
>
<MaterialIcons name="close" size={12} />
</View>
</TouchableOpacity>
</View>
<Text style={{ ...texts.xl, textAlign: 'center', marginTop: halfPadding }}>
{t('Seu endereço de entrega não bate com a sua localização.')}
</Text>
<Text style={{ ...texts.md, textAlign: 'center', marginTop: padding }}>
{t('Você quer continuar mesmo assim?')}
</Text>
<DefaultButton style={{ marginTop: padding }} title={t('Sim')} onPress={positiveAnswer} />
<DefaultButton
style={{ marginTop: halfPadding, backgroundColor: colors.white }}
title={t('Não, quero cancelar')}
onPress={negativeAnswer}
/>
</PaddedView>
</View>
</Modal>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useContextGetSeverTime } from '../../../../common/contexts/ServerTimeCo
export const useBusinessIsAcceptingOrders = (business?: Business) => {
const platformParams = usePlatformParamsContext();
const getServerTime = useContextGetSeverTime();

if (!platformParams) return false;
if (!getServerTime) return false;
if (!business) return false;
Expand Down