This commit is contained in:
shafi54 2026-02-06 00:54:21 +05:30
parent aa7035b77c
commit f80f234b98
7 changed files with 142 additions and 11 deletions

File diff suppressed because one or more lines are too long

View file

@ -114,6 +114,7 @@ export const commonRouter = router({
productQuantity: product.productQuantity,
storeId: product.store?.id || null,
isOutOfStock: product.isOutOfStock,
isFlashAvailable: product.isFlashAvailable,
nextDeliveryDate: nextDeliveryDate ? nextDeliveryDate.toISOString() : null,
images: product.images, // Already signed URLs from cache
};

View file

@ -1,10 +1,59 @@
import React, { useEffect } from 'react';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { View, Alert } from 'react-native';
import { FlashDeliveryProducts } from '@/components/SlotSpecificView';
import { useSlotStore } from '@/components/stores/slotStore';
import FlashDeliveryNote from '@/components/FlashDeliveryNote';
import { useFlashNavigationStore } from '@/components/stores/flashNavigationStore';
import { useFocusEffect } from '@react-navigation/native';
import { useFlashCartStore } from '@/src/store/flashCartStore';
import { tw, BottomDialog, MyText, MyTouchableOpacity } from 'common-ui';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { useAddToCart } from '@/hooks/cart-query-hooks';
const FlashAddToCartDialog = () => {
const { addedFlashProduct, clearAddedFlashProduct } = useFlashCartStore();
const addToCart = useAddToCart({
showSuccessAlert: false,
showErrorAlert: false,
refetchCart: true,
}, "flash");
const product = addedFlashProduct?.product;
const isOpen = !!addedFlashProduct;
React.useEffect(() => {
if (isOpen && product) {
addToCart.mutate(
{ productId: product.id, quantity: 1, slotId: 0 },
{
onSuccess: () => {
clearAddedFlashProduct();
setTimeout(() => {
Alert.alert('Added to Cart', `Added ${product.name} for delivery in 1hr`);
}, 300);
}
}
);
}
}, [isOpen, product]);
if (!isOpen || !product) return null;
return null;
return (
<BottomDialog open={isOpen} onClose={clearAddedFlashProduct}>
<View style={tw`p-6 items-center`}>
<View style={tw`w-16 h-16 bg-pink-50 rounded-full items-center justify-center mb-4`}>
<MaterialIcons name="bolt" size={32} color="#f81260" />
</View>
<MyText style={tw`text-xl font-bold text-gray-900 mb-2`}>1 hr Delivery</MyText>
<MyText style={tw`text-gray-600 text-center mb-4`}>Adding {product.name}...</MyText>
</View>
</BottomDialog>
);
};
export default function FlashDeliveryView() {
const { storeId } = useLocalSearchParams<{ storeId?: string }>();
@ -37,6 +86,7 @@ export default function FlashDeliveryView() {
baseUrl="/(drawer)/(tabs)/flash-delivery"
onProductPress={handleProductPress}
/>
<FlashAddToCartDialog />
</>
);
}
}

View file

@ -104,7 +104,11 @@ const ProductCard: React.FC<ProductCardProps> = ({
Alert.alert("Error", "No available delivery slot for this product");
return;
}
addToCart(item.id, 1, slotId, () => {});
const slot = slotMap[slotId];
const deliveryTime = slot ? dayjs(slot.deliveryTime).format('ddd, DD MMM • h:mm A') : '';
addToCart(item.id, 1, slotId, () => {
Alert.alert('Added to Cart', `Added ${item.name} for delivery at ${deliveryTime}`);
});
} else if (cartItem) {
updateCartItem.mutate({ itemId: cartItem.id, quantity: newQuantity });
}

View file

@ -354,8 +354,14 @@ export function SlotProducts({ slotId:slotIdParent, storeId:storeIdParent, baseU
const handleAddToCart = (productId: number) => {
setIsLoadingDialogOpen(true);
addToCart(productId, 1, slotId || 0, () => setIsLoadingDialogOpen(false));
const item = filteredProducts.find((p: any) => p.id === productId);
const deliveryTime = slotQuery.data?.deliveryTime ? dayjs(slotQuery.data.deliveryTime).format('ddd, DD MMM • h:mm A') : '';
addToCart(productId, 1, slotId || 0, () => {
setIsLoadingDialogOpen(false);
if (item) {
Alert.alert('Added to Cart', `Added ${item.name} for delivery at ${deliveryTime}`);
}
});
};
if (slotQuery.isLoading || (storeIdNum && productsQuery?.isLoading)) {
@ -441,8 +447,13 @@ export function FlashDeliveryProducts({ storeId:storeIdParent, baseUrl, onProduc
const handleAddToCart = (productId: number) => {
setIsLoadingDialogOpen(true);
addToCart(productId, 1, 0, () => setIsLoadingDialogOpen(false));
const item = flashProducts.find((p: any) => p.id === productId);
addToCart(productId, 1, 0, () => {
setIsLoadingDialogOpen(false);
if (item) {
Alert.alert('Added to Cart', `Added ${item.name} for delivery in 1hr`);
}
});
};
if (storeIdNum && productsQuery?.isLoading) {

View file

@ -1,19 +1,29 @@
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 } from '@/hooks/cart-query-hooks';
import { useGetEssentialConsts } from '@/src/api-hooks/essential-consts.api';
import dayjs from 'dayjs';
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,
@ -32,6 +42,7 @@ export default function AddToCartDialog() {
const product = addedToCartProduct?.product;
// Pre-select cart's slotId and quantity if item is already in cart
useEffect(() => {
if (isOpen && product) {
@ -81,7 +92,16 @@ export default function AddToCartDialog() {
// 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 },
@ -100,7 +120,7 @@ export default function AddToCartDialog() {
return (
<BottomDialog open={isOpen} onClose={clearAddedToCartProduct}>
<View style={tw`p-6 max-h-[500px]`}>
<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" />
@ -120,7 +140,10 @@ export default function AddToCartDialog() {
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)}
onPress={() => {
setSelectedSlotId(slot.id);
setSelectedFlashDelivery(false);
}}
activeOpacity={0.7}
>
<MaterialIcons name="local-shipping" size={20} color="#3B82F6" style={tw`mt-0.5`} />
@ -138,6 +161,30 @@ export default function AddToCartDialog() {
))}
</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>
<Quantifier
@ -150,9 +197,9 @@ export default function AddToCartDialog() {
<View style={tw`flex-row gap-3 mt-4`}>
<MyTouchableOpacity
style={tw`flex-1 bg-brand500 py-3.5 rounded-xl items-center ${!selectedSlotId ? 'opacity-50' : ''}`}
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}
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')}

View file

@ -0,0 +1,18 @@
import { create } from 'zustand';
interface AddedFlashProduct {
productId: number;
product: any;
}
interface FlashCartStore {
addedFlashProduct: AddedFlashProduct | null;
setAddedFlashProduct: (product: AddedFlashProduct | null) => void;
clearAddedFlashProduct: () => void;
}
export const useFlashCartStore = create<FlashCartStore>((set) => ({
addedFlashProduct: null,
setAddedFlashProduct: (product) => set({ addedFlashProduct: product }),
clearAddedFlashProduct: () => set({ addedFlashProduct: null }),
}));