freshyo/apps/user-ui/src/components/AddToCartDialog.tsx
2026-03-10 13:05:33 +05:30

261 lines
9.7 KiB
TypeScript

import React, { useState, useMemo, useEffect } from 'react';
import { View, ScrollView } from 'react-native';
import { useRouter } from 'expo-router';
import { tw, BottomDialog, MyText, MyTouchableOpacity, Quantifier } from 'common-ui';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { useCartStore } from '@/src/store/cartStore';
import { useFlashCartStore } from '@/src/store/flashCartStore';
import { trpc } from '@/src/trpc-client';
import { useAddToCart, useGetCart, useUpdateCartItem, useRemoveFromCart } from '@/hooks/cart-query-hooks';
import { useGetEssentialConsts } from '@/src/hooks/prominent-api-hooks';
import dayjs from 'dayjs';
import { SafeAreaView } from 'react-native-safe-area-context';
const formatTimeRange = (deliveryTime: string) => {
const time = dayjs(deliveryTime);
const endTime = time.add(1, 'hour');
const startPeriod = time.format('A');
const endPeriod = endTime.format('A');
if (startPeriod === endPeriod) {
return `${time.format('h')}-${endTime.format('h')} ${startPeriod}`;
} else {
return `${time.format('h:mm')} ${startPeriod} - ${endTime.format('h:mm')} ${endPeriod}`;
}
};
export default function AddToCartDialog() {
const router = useRouter();
const { addedToCartProduct, clearAddedToCartProduct } = useCartStore();
const [quantity, setQuantity] = useState(1);
const [selectedSlotId, setSelectedSlotId] = useState<number | null>(null);
const [selectedFlashDelivery, setSelectedFlashDelivery] = useState(false);
const { data: slotsData } = trpc.user.slots.getSlotsWithProducts.useQuery();
const { data: cartData } = useGetCart();
const { data: constsData } = useGetEssentialConsts();
// const isFlashDeliveryEnabled = constsData?.isFlashDeliveryEnabled === true;
const isFlashDeliveryEnabled = true;
const addToCart = useAddToCart({
showSuccessAlert: false,
showErrorAlert: false,
refetchCart: true,
});
const updateCartItem = useUpdateCartItem({
showSuccessAlert: false,
showErrorAlert: false,
refetchCart: true,
});
const removeFromCart = useRemoveFromCart({
showSuccessAlert: false,
showErrorAlert: false,
refetchCart: true,
});
const isOpen = !!addedToCartProduct;
const product = addedToCartProduct?.product;
// Pre-select cart's slotId and quantity if item is already in cart
useEffect(() => {
if (isOpen && product) {
const cartItem = cartData?.items?.find((item: any) => item.productId === product.id);
const cartQuantity = cartItem?.quantity || 0;
// Set quantity: 0 → 1, >1 → keep as is
setQuantity(cartQuantity === 0 ? 1 : cartQuantity);
if (cartItem?.slotId) {
setSelectedSlotId(cartItem.slotId);
} else {
setSelectedSlotId(null);
}
}
}, [isOpen, cartData, product]);
const { slotMap, productSlotIdsMap } = useMemo(() => {
const slotMap: Record<number, any> = {};
const productSlotIdsMap: Record<number, number[]> = {};
if (slotsData?.slots) {
slotsData.slots.forEach((slot: any) => {
slotMap[slot.id] = slot;
slot.products?.forEach((p: any) => {
if (!productSlotIdsMap[p.id]) {
productSlotIdsMap[p.id] = [];
}
productSlotIdsMap[p.id].push(slot.id);
});
});
}
return { slotMap, productSlotIdsMap };
}, [slotsData]);
const availableSlotIds = productSlotIdsMap[product?.id] || [];
const availableSlots = availableSlotIds
.map((slotId) => slotMap[slotId])
.filter(Boolean);
// Find cart item for this product
const cartItem = cartData?.items?.find((item: any) => item.productId === product?.id);
// Determine if updating existing item (quantity > 1 means it's an update)
const isUpdate = (cartItem?.quantity || 0) >= 1;
// Check if flash delivery option should be shown
const showFlashOption = product?.isFlashAvailable === true && isFlashDeliveryEnabled;
const handleAddToCart = () => {
if (selectedFlashDelivery) {
useFlashCartStore.getState().setAddedFlashProduct({ productId: product.id, product });
router.push('/(drawer)/(tabs)/flash-delivery/(products)');
clearAddedToCartProduct();
return;
}
if (isUpdate && cartItem?.id) {
updateCartItem.mutate(
{ itemId: cartItem.id, quantity },
{ onSuccess: () => clearAddedToCartProduct() }
);
} else {
const slotId = selectedSlotId ?? availableSlotIds[0] ?? 0;
addToCart.mutate(
{ productId: product.id, quantity, slotId },
{ onSuccess: () => clearAddedToCartProduct() }
);
}
};
if (!isOpen || !addedToCartProduct) return null;
return (
<BottomDialog open={isOpen} onClose={clearAddedToCartProduct}>
<SafeAreaView>
<View style={tw`p-6 max-h-[650px]`}>
<View style={tw`flex-row items-center mb-2`}>
<View style={tw`w-10 h-10 bg-blue-50 rounded-full items-center justify-center mr-3`}>
<MaterialIcons name="schedule" size={20} color="#3B82F6" />
</View>
<View>
<MyText style={tw`text-xl font-bold text-gray-900`}>Select Delivery Slot</MyText>
{product?.name && (
<MyText style={tw`text-sm text-gray-500`}>
{product.name} ({product.productQuantity}{product.unitNotation ? ` ${product.unitNotation}` : ''})
</MyText>
)}
</View>
</View>
<ScrollView showsVerticalScrollIndicator={false}>
{availableSlots.map((slot: any) => (
<MyTouchableOpacity
key={slot.id}
style={tw`flex-row items-start mb-4 bg-gray-50 p-4 rounded-xl border border-gray-100 ${selectedSlotId === slot.id ? 'border-brand500' : 'border-gray-100'
}`}
onPress={() => {
setSelectedSlotId(slot.id);
setSelectedFlashDelivery(false);
}}
activeOpacity={0.7}
>
<MaterialIcons name="local-shipping" size={20} color="#3B82F6" style={tw`mt-0.5`} />
<View style={tw`ml-3 flex-1`}>
<MyText style={tw`text-gray-900 font-bold text-base`}>
{dayjs(slot.deliveryTime).format('ddd, DD MMM • ')}{formatTimeRange(slot.deliveryTime)}
</MyText>
</View>
{selectedSlotId === slot.id ? (
<MaterialIcons name="check-circle" size={24} color="#3B82F6" style={tw`mt-0.5`} />
) : (
<MaterialIcons name="check-box-outline-blank" size={24} color="#9CA3AF" style={tw`mt-0.5`} />
)}
</MyTouchableOpacity>
))}
</ScrollView>
{showFlashOption && (
<MyTouchableOpacity
key="flash-delivery"
style={tw`flex-row items-center mb-4 bg-pink-50 p-4 rounded-xl border ${selectedFlashDelivery ? 'border-pink-500' : 'border-pink-200'
}`}
onPress={() => {
setSelectedFlashDelivery(true);
setSelectedSlotId(null);
}}
activeOpacity={0.7}
>
<MaterialIcons name="bolt" size={20} color="#f81260" />
<View style={tw`ml-3 flex-1`}>
<MyText style={tw`text-gray-900 font-bold text-base`}>1 hr Delivery</MyText>
</View>
{selectedFlashDelivery ? (
<MaterialIcons name="check-circle" size={24} color="#f81260" />
) : (
<MaterialIcons name="check-box-outline-blank" size={24} color="#f81260" />
)}
</MyTouchableOpacity>
)}
<View style={tw`mt-4`}>
<MyText style={tw`text-sm font-bold text-gray-900 mb-2`}>Quantity</MyText>
<View style={tw`flex-row items-center`}>
<View style={tw`flex-1`}>
<Quantifier
value={quantity}
setValue={setQuantity}
step={1}
unit={product.unitNotation}
/>
</View>
{isUpdate && (
<MyTouchableOpacity
onPress={() => {
if (cartItem?.id) {
removeFromCart.mutate(
{ itemId: cartItem.id },
{ onSuccess: () => clearAddedToCartProduct() }
);
} else {
clearAddedToCartProduct();
}
}}
style={tw`p-2 ml-3 bg-red-50 rounded-lg border border-red-200`}
>
<MaterialIcons name="delete-outline" size={24} color="#EF4444" />
</MyTouchableOpacity>
)}
</View>
</View>
<View style={tw`flex-row gap-3 mt-4 pb-12`}>
<MyTouchableOpacity
style={tw`flex-1 bg-brand500 py-3.5 rounded-xl items-center ${(!selectedSlotId && !selectedFlashDelivery) ? 'opacity-50' : ''}`}
onPress={handleAddToCart}
disabled={(addToCart.isLoading || updateCartItem.isLoading) || (!selectedSlotId && !selectedFlashDelivery)}
>
<MyText style={tw`text-white font-bold`}>
{addToCart.isLoading || updateCartItem.isLoading ? (isUpdate ? 'Updating...' : 'Adding...') : (isUpdate ? 'Update Item' : 'Add to Cart')}
</MyText>
</MyTouchableOpacity>
<MyTouchableOpacity
style={tw`flex-1 bg-gray-100 py-3.5 rounded-xl items-center`}
onPress={clearAddedToCartProduct}
>
<MyText style={tw`text-gray-700 font-bold`}>Cancel</MyText>
</MyTouchableOpacity>
</View>
</View>
</SafeAreaView>
</BottomDialog>
);
}