freshyo/apps/user-ui/components/QuickDeliveryAddressSelector.tsx
2026-01-24 00:13:15 +05:30

277 lines
No EOL
11 KiB
TypeScript

import React, { useState } from 'react';
import { View, ScrollView, Dimensions } from 'react-native';
import { useRouter } from 'expo-router';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { BottomDialog, MyTouchableOpacity, MyText, tw, theme } from 'common-ui';
import { useAuth } from '@/src/contexts/AuthContext';
import { trpc } from '@/src/trpc-client';
import { useAddressStore } from '@/src/store/addressStore';
import dayjs from 'dayjs';
interface QuickDeliveryAddressSelectorProps {
deliveryTime?: string;
slotId?: number;
onSlotChange?: (slotId: number) => void;
isForFlashDelivery?: boolean;
}
const QuickDeliveryAddressSelector: React.FC<QuickDeliveryAddressSelectorProps> = ({
deliveryTime,
slotId,
onSlotChange,
isForFlashDelivery = false
}) => {
const router = useRouter();
const [dialogOpen, setDialogOpen] = useState(false);
const { isAuthenticated } = useAuth();
const { selectedAddressId, setSelectedAddressId } = useAddressStore();
const { data: defaultAddressData } = trpc.user.address.getDefaultAddress.useQuery();
const { data: addressesData } = trpc.user.address.getUserAddresses.useQuery(undefined, {
enabled: isAuthenticated,
});
const { data: slotsData } = trpc.user.slots.getSlotsWithProducts.useQuery();
const defaultAddress = defaultAddressData?.data;
const addresses = addressesData?.data || [];
// Find earliest slot for pre-selection
const earliestSlot = slotsData?.slots?.sort((a, b) =>
dayjs(a.deliveryTime).diff(dayjs(b.deliveryTime))
)[0];
// Transform addresses for display
const addressOptions = addresses.map(address => ({
id: address.id,
name: address.name,
address: `${address.addressLine1}${address.addressLine2 ? `, ${address.addressLine2}` : ''}, ${address.city}, ${address.state} - ${address.pincode}`,
phone: address.phone,
}));
// Transform slots for display
const slotOptions = slotsData?.slots?.map(slot => ({
id: slot.id,
deliveryTime: dayjs(slot.deliveryTime).format('MMM DD, h:mm A'),
closeTime: dayjs(slot.freezeTime).format('h:mm A'),
})) || [];
// Get current selected slot display
const getCurrentSlotDisplay = () => {
if (isForFlashDelivery) return '30 minutes';
if (slotId) {
const slot = slotsData?.slots?.find(s => s.id === slotId);
return slot ? dayjs(slot.deliveryTime).format('MMM DD, h:mm A') : 'Select time';
}
if (earliestSlot) {
return dayjs(earliestSlot.deliveryTime).format('MMM DD, h:mm A');
}
return 'Select time';
};
// Get current selected address display
const getCurrentAddressDisplay = () => {
if (selectedAddressId) {
const address = addresses.find(a => a.id === selectedAddressId);
if (address) return `${address.name} - ${address.addressLine1}`;
}
if (defaultAddress) {
return `${defaultAddress.name} - ${defaultAddress.addressLine1}`;
}
return 'Select address';
};
return (
<>
{isForFlashDelivery && (
<View style={tw`flex-row items-center mb-1`}>
<MyTouchableOpacity
onPress={() => router.back()}
style={tw`p-2 -ml-2`}
activeOpacity={0.7}
>
<MaterialIcons name="chevron-left" size={24} color="#f81260" />
</MyTouchableOpacity>
<MyText style={[tw`text-lg font-bold ml-2`, { color: '#f81260' }]}>
Delivery in 30 minutes
</MyText>
</View>
)}
{!isForFlashDelivery && (
<View style={tw`flex-1 mr-2`}>
<View style={tw`flex-row items-center mb-1`}>
<MyTouchableOpacity
onPress={() => router.back()}
style={tw`p-2 -ml-2`}
activeOpacity={0.7}
>
<MaterialIcons name="chevron-left" size={24} />
</MyTouchableOpacity>
<MyText style={[tw`text-lg font-bold ml-2`]}>
Delivery At {getCurrentSlotDisplay()}
</MyText>
</View>
{/* Trigger Component with Separate Chevrons */}
<View style={tw`bg-brand50 border border-brand100 rounded-lg p-3`}>
/* Regular Delivery Time Section */
<MyTouchableOpacity
onPress={() => setDialogOpen(true)}
style={tw`flex-row items-center justify-between mb-2`}
activeOpacity={0.7}
>
<View style={tw`flex-1`}>
{/* <MyText style={tw`text-xs text-brand500 font-bold uppercase tracking-wider leading-3`}>
Delivery Time
</MyText> */}
<MyText style={tw`text-sm font-bold text-brand900 leading-4`}>
Delivery at: {getCurrentSlotDisplay()}
</MyText>
</View>
<MaterialIcons name="keyboard-arrow-down" size={20} color={theme.colors.brand500} />
</MyTouchableOpacity>
{/* Address Section */}
<MyTouchableOpacity
onPress={() => setDialogOpen(true)}
style={tw`flex-row items-center justify-between`}
activeOpacity={0.7}
>
<View style={tw`flex-1`}>
{/* <MyText style={tw`text-xs text-brand500 font-bold uppercase tracking-wider leading-3`}>
Delivery Address
</MyText> */}
<MyText style={tw`text-sm font-bold text-brand900 leading-4`} numberOfLines={1}>
TO: {getCurrentAddressDisplay()}
</MyText>
</View>
<MaterialIcons name="keyboard-arrow-down" size={20} color={theme.colors.brand500} />
</MyTouchableOpacity>
</View>
{/* Consolidated Dialog - 80% height */}
<BottomDialog
open={dialogOpen}
onClose={() => setDialogOpen(false)}
>
<View style={[tw`py-6`, { height: Dimensions.get('window').height * 0.8 }]}>
<MyText style={tw`text-xl font-bold text-gray-900 mb-6 text-center`}>
Select Delivery Options
</MyText>
<View style={tw`flex-1`}>
{/* Section 1: Delivery Time Selection */}
<View style={tw`mb-6`}>
<MyText style={tw`text-lg font-bold text-gray-900 mb-3`}>
Select Delivery Time
</MyText>
<ScrollView style={tw`max-h-40`} showsVerticalScrollIndicator={false}>
{isForFlashDelivery ? (
<View style={tw`p-3 bg-green-50 border border-green-200 rounded-lg mb-2`}>
<View style={tw`flex-row items-center`}>
<MaterialIcons name="bolt" size={20} color="#16a34a" />
<MyText style={tw`text-green-800 font-medium ml-2`}>
Flash Delivery - 30 minutes
</MyText>
</View>
<MyText style={tw`text-green-700 text-xs mt-1`}>
Selected automatically for flash delivery
</MyText>
</View>
) : (
slotOptions.map(slot => (
<MyTouchableOpacity
key={slot.id}
style={tw`p-3 border border-gray-200 rounded-lg mb-2 ${
slot.id === (slotId || earliestSlot?.id) ? 'bg-brand50 border-brand500' : 'bg-white'
}`}
onPress={() => {
onSlotChange?.(slot.id);
setDialogOpen(false);
}}
activeOpacity={0.7}
>
<MyText style={tw`font-medium text-gray-900`}>
Delivery: {slot.deliveryTime}
</MyText>
<MyText style={tw`text-xs text-gray-500 mt-1`}>
Orders Close at: {slot.closeTime}
</MyText>
</MyTouchableOpacity>
))
)}
</ScrollView>
</View>
{/* Divider */}
<View style={tw`h-px bg-gray-200 mb-6`} />
{/* Section 2: Address Selection */}
<View style={tw`flex-1`}>
<MyText style={tw`text-lg font-bold text-gray-900 mb-3`}>
Select Delivery Address
</MyText>
<ScrollView style={tw`flex-1`} showsVerticalScrollIndicator={false}>
{!isAuthenticated ? (
<View style={tw`p-4 bg-red-50 border border-red-200 rounded-lg`}>
<View style={tw`flex-row items-center`}>
<MaterialIcons name="error" size={20} color="#DC2626" />
<MyText style={tw`text-red-800 font-medium ml-2`}>
Authentication Required
</MyText>
</View>
<MyText style={tw`text-red-700 text-sm mt-2`}>
Please log in to select and manage delivery addresses.
</MyText>
</View>
) : addressOptions.length === 0 ? (
<View style={tw`p-4 bg-gray-50 border border-gray-200 rounded-lg`}>
<MyText style={tw`text-gray-600 text-center`}>
No delivery addresses available. Please add an address first.
</MyText>
</View>
) : (
addressOptions.map(address => (
<MyTouchableOpacity
key={address.id}
style={tw`p-3 border border-gray-200 rounded-lg mb-2 ${
address.id === (selectedAddressId || defaultAddress?.id) ? 'bg-brand50 border-brand500' : 'bg-white'
}`}
onPress={() => {
setSelectedAddressId(address.id);
setDialogOpen(false);
}}
activeOpacity={0.7}
>
<MyText style={tw`font-medium text-gray-900`}>
{address.name}
</MyText>
<MyText style={tw`text-sm text-gray-600 mt-1`} numberOfLines={2}>
{address.address}
</MyText>
<MyText style={tw`text-xs text-gray-500 mt-1`}>
Phone: {address.phone}
</MyText>
</MyTouchableOpacity>
))
)}
</ScrollView>
</View>
</View>
</View>
</BottomDialog>
</View>
)}
</>
);
};
export default QuickDeliveryAddressSelector;