freshyo/apps/user-ui/app/(drawer)/(tabs)/me/addresses/index.tsx
2026-01-24 00:13:15 +05:30

278 lines
No EOL
8.9 KiB
TypeScript

import React, { useState } from 'react';
import { View, Alert, Modal } from 'react-native';
import { useRouter } from 'expo-router';
import { AppContainer, MyText, tw, useMarkDataFetchers, MyFlatList, MyTouchableOpacity } from 'common-ui';
import { trpc } from '@/src/trpc-client';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import AddressForm from '@/src/components/AddressForm';
interface Address {
id: number;
name: string;
phone: string;
addressLine1: string;
addressLine2: string | null;
city: string;
state: string;
pincode: string;
isDefault: boolean;
}
function AddressCard({ address, onEdit, onDelete, onSetDefault, isDeleting }: {
address: Address;
onEdit: (address: Address) => void;
onDelete: (id: number) => void;
onSetDefault: (id: number) => void;
isDeleting?: boolean;
}) {
const formatAddress = (addr: Address) => {
return `${addr.addressLine1}${addr.addressLine2 ? `, ${addr.addressLine2}` : ''}, ${addr.city}, ${addr.state} - ${addr.pincode}`;
};
return (
<View style={tw`bg-white rounded-lg p-4 mb-3 shadow-sm border border-gray-100`}>
<View style={tw`flex-row justify-between items-start mb-2`}>
<View style={tw`flex-1`}>
<View style={tw`flex-row items-center mb-1`}>
<MyText weight="semibold" style={tw`text-lg text-gray-800 mr-2`}>
{address.name}
</MyText>
{address.isDefault && (
<View style={tw`bg-green-100 px-2 py-1 rounded-full`}>
<MyText style={tw`text-green-700 text-xs font-medium`}>Default</MyText>
</View>
)}
</View>
<MyText style={tw`text-gray-600 text-sm mb-1`}>
📞 {address.phone}
</MyText>
<MyText style={tw`text-gray-700 text-sm`}>
📍 {formatAddress(address)}
</MyText>
</View>
</View>
<View style={tw`flex-row justify-end mt-3 space-x-2`}>
{!address.isDefault && (
<MyTouchableOpacity
style={tw`bg-gray-500 px-3 py-2 rounded mr-2`}
onPress={() => onSetDefault(address.id)}
>
<MyText style={tw`text-white text-xs font-medium`}>Set Default</MyText>
</MyTouchableOpacity>
)}
<MyTouchableOpacity
style={tw`bg-blue-500 px-3 py-2 rounded mr-2`}
onPress={() => onEdit(address)}
>
<MyText style={tw`text-white text-xs font-medium`}>Edit</MyText>
</MyTouchableOpacity>
<MyTouchableOpacity
style={tw`bg-red-500 px-3 py-2 rounded ${isDeleting ? 'opacity-50' : ''}`}
onPress={() => onDelete(address.id)}
disabled={isDeleting}
>
<MyText style={tw`text-white text-xs font-medium`}>
{isDeleting ? 'Deleting...' : 'Delete'}
</MyText>
</MyTouchableOpacity>
</View>
</View>
);
}
export default function Addresses() {
const [modalVisible, setModalVisible] = useState(false);
const [editingAddress, setEditingAddress] = useState<Address | null>(null);
const { data, isLoading, error, refetch } = trpc.user.address.getUserAddresses.useQuery();
useMarkDataFetchers(() => {
refetch();
});
const addresses = data?.data || [];
const updateAddressMutation = trpc.user.address.updateAddress.useMutation();
const deleteAddressMutation = trpc.user.address.deleteAddress.useMutation();
const handleAddAddress = () => {
setEditingAddress(null);
setModalVisible(true);
};
const handleEditAddress = (address: Address) => {
setEditingAddress(address);
setModalVisible(true);
};
const handleDeleteAddress = (id: number) => {
Alert.alert(
'Delete Address',
'Are you sure you want to delete this address?',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Delete',
style: 'destructive',
onPress: () => {
deleteAddressMutation.mutate({ id }, {
onSuccess: () => {
refetch();
Alert.alert('Success', 'Address deleted successfully');
},
onError: (error) => {
Alert.alert('Error', error.message || 'Failed to delete address');
}
});
}
}
]
);
};
const handleSetDefault = (id: number) => {
// Update the address to be default
const addressToUpdate = addresses.find(addr => addr.id === id);
if (addressToUpdate) {
updateAddressMutation.mutate({
id: addressToUpdate.id,
name: addressToUpdate.name,
phone: addressToUpdate.phone,
addressLine1: addressToUpdate.addressLine1,
addressLine2: addressToUpdate.addressLine2 || undefined,
city: addressToUpdate.city,
state: addressToUpdate.state,
pincode: addressToUpdate.pincode,
isDefault: true,
}, {
onSuccess: () => {
refetch();
Alert.alert('Success', 'Default address updated');
},
onError: (error) => {
Alert.alert('Error', error.message || 'Failed to update default address');
}
});
}
};
const handleAddressSubmit = () => {
setModalVisible(false);
setEditingAddress(null);
refetch();
};
if (isLoading) {
return (
<AppContainer>
<View style={tw`flex-1 justify-center items-center`}>
<MyText style={tw`text-gray-600`}>Loading addresses...</MyText>
</View>
</AppContainer>
);
}
if (error) {
return (
<AppContainer>
<View style={tw`flex-1 justify-center items-center p-4`}>
<MaterialIcons name="error-outline" size={48} color="#EF4444" />
<MyText style={tw`text-red-600 text-center mt-2 mb-4`}>
Failed to load addresses. Please try again.
</MyText>
<MyTouchableOpacity
style={tw`bg-blue-500 px-4 py-2 rounded`}
onPress={() => refetch()}
>
<MyText style={tw`text-white font-medium`}>Retry</MyText>
</MyTouchableOpacity>
</View>
</AppContainer>
);
}
return (
<>
<MyFlatList
data={addresses}
renderItem={({ item }) => (
<AddressCard
address={item}
onEdit={handleEditAddress}
onDelete={handleDeleteAddress}
onSetDefault={handleSetDefault}
/>
)}
keyExtractor={(item) => item.id.toString()}
ListHeaderComponent={() => (
<View style={tw`flex-row justify-between items-center p-4 pb-2`}>
<MyText weight="bold" style={tw`text-2xl text-gray-800`}>
My Addresses
</MyText>
<MyTouchableOpacity
style={tw`bg-blue-500 p-2 rounded-full`}
onPress={handleAddAddress}
>
<MaterialIcons name="add" size={24} color="white" />
</MyTouchableOpacity>
</View>
)}
ListEmptyComponent={() => (
<View style={tw`justify-center items-center py-12`}>
<MaterialIcons name="location-off" size={64} color="#9CA3AF" />
<MyText style={tw`text-gray-500 text-center mt-4 mb-2`}>
No addresses found
</MyText>
<MyText style={tw`text-gray-400 text-center mb-6`}>
Add your first delivery address
</MyText>
<MyTouchableOpacity
style={tw`bg-blue-500 px-6 py-3 rounded-lg`}
onPress={handleAddAddress}
>
<MyText style={tw`text-white font-medium`}>Add Address</MyText>
</MyTouchableOpacity>
</View>
)}
contentContainerStyle={tw`p-4`}
showsVerticalScrollIndicator={false}
/>
<Modal
visible={modalVisible}
animationType="slide"
presentationStyle="pageSheet"
onRequestClose={() => setModalVisible(false)}
>
<AppContainer>
<View style={tw`flex-row justify-between items-center pb-2`}>
<MyText weight="semibold" style={tw`text-lg text-gray-800`}>
{editingAddress ? 'Edit Address' : 'Add Address'}
</MyText>
<MyTouchableOpacity
onPress={() => setModalVisible(false)}
style={tw`p-1`}
>
<MaterialIcons name="close" size={24} color="#6B7280" />
</MyTouchableOpacity>
</View>
<AddressForm
onSuccess={handleAddressSubmit}
initialValues={editingAddress ? {
name: editingAddress.name,
phone: editingAddress.phone,
addressLine1: editingAddress.addressLine1,
addressLine2: editingAddress.addressLine2 || '',
city: editingAddress.city,
state: editingAddress.state,
pincode: editingAddress.pincode,
isDefault: editingAddress.isDefault,
} : undefined}
isEdit={!!editingAddress}
/>
</AppContainer>
</Modal>
</>
);
}