This commit is contained in:
shafi54 2026-01-30 00:17:12 +05:30
parent e96ed2334e
commit d658022a51
9 changed files with 4056 additions and 346 deletions

View file

@ -27,6 +27,9 @@ import { trpc } from "@/src/trpc-client";
import MaterialIcons from "@expo/vector-icons/MaterialIcons"; import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import Entypo from "@expo/vector-icons/Entypo"; import Entypo from "@expo/vector-icons/Entypo";
import * as Location from "expo-location"; import * as Location from "expo-location";
import { OrderOptionsMenu } from "@/components/OrderOptionsMenu";
import CancelOrderDialog from "@/components/CancelOrderDialog";
import { OrderNotesForm } from "@/components/OrderNotesForm";
// Define types outside // Define types outside
interface OrderWithSequence { interface OrderWithSequence {
@ -270,6 +273,8 @@ export default function DeliverySequences() {
const [selectedUserId, setSelectedUserId] = useState<number>(-1); const [selectedUserId, setSelectedUserId] = useState<number>(-1);
const [selectedOrderIds, setSelectedOrderIds] = useState<number[]>([]); const [selectedOrderIds, setSelectedOrderIds] = useState<number[]>([]);
const [hasSequenceChanged, setHasSequenceChanged] = useState(false); const [hasSequenceChanged, setHasSequenceChanged] = useState(false);
const [cancelDialogOpen, setCancelDialogOpen] = useState(false);
const [notesDialogOpen, setNotesDialogOpen] = useState(false);
const router = useRouter(); const router = useRouter();
const { data: slotsData, refetch: refetchSlots } = const { data: slotsData, refetch: refetchSlots } =
@ -705,273 +710,90 @@ export default function DeliverySequences() {
)} )}
{/* Order Menu Dialog */} {/* Order Menu Dialog */}
<BottomDialog <OrderOptionsMenu
open={showOrderMenu} open={showOrderMenu}
onClose={() => setShowOrderMenu(false)} onClose={() => setShowOrderMenu(false)}
> order={{
<View style={tw`pb-8 pt-2 px-4`}> id: selectedOrder?.id || 0,
{/* Handle Bar */} readableId: selectedOrder?.readableId || 0,
<View style={tw`items-center mb-6`}> isPackaged: selectedOrder?.isPackaged || false,
<View style={tw`w-12 h-1.5 bg-gray-200 rounded-full mb-4`} /> isDelivered: selectedOrder?.isDelivered || false,
<MyText style={tw`text-lg font-bold text-gray-900`}> address: selectedOrder?.address || '',
Order #{selectedOrder?.readableId} addressId: selectedOrder?.addressId || 0,
</MyText> adminNotes: selectedOrder?.adminNotes || null,
<MyText style={tw`text-sm text-gray-500`}> latitude: selectedOrder?.latitude || null,
Select an action to perform longitude: selectedOrder?.longitude || null,
</MyText> }}
</View> onViewDetails={() => {
if (selectedOrder) {
{/* Actions */} router.push(`/order-details/${selectedOrder.id}`);
<TouchableOpacity }
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm`}
onPress={() => {
router.push(`/order-details/${selectedOrder?.id}`);
setShowOrderMenu(false); setShowOrderMenu(false);
}} }}
disabled={updateAddressCoordsMutation.isPending} onTogglePackaged={() => {
>
<View
style={tw`w-10 h-10 rounded-full bg-purple-50 items-center justify-center mr-4`}
>
<MaterialIcons name="visibility" size={20} color="#9333ea" />
</View>
<View>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
View Details
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
See full order information
</MyText>
</View>
<MaterialIcons
name="chevron-right"
size={24}
color="#9ca3af"
style={tw`ml-auto`}
/>
</TouchableOpacity>
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm ${
updatePackagedMutation.isPending ? "opacity-50" : ""
}`}
onPress={async () => {
if (!selectedOrder) return; if (!selectedOrder) return;
try { updatePackagedMutation.mutate(
await updatePackagedMutation.mutateAsync({ { orderId: selectedOrder.id.toString(), isPackaged: !selectedOrder.isPackaged },
orderId: selectedOrder.id.toString(), {
isPackaged: !selectedOrder.isPackaged, onSuccess: () => {
});
refetchOrders(); refetchOrders();
refetchSequence(); refetchSequence();
setShowOrderMenu(false); },
} catch (error) { onError: () => {
Alert.alert("Error", "Failed to update packaged status"); Alert.alert("Error", "Failed to update packaged status");
},
} }
);
}} }}
disabled={updatePackagedMutation.isPending} onToggleDelivered={() => {
>
<View
style={tw`w-10 h-10 rounded-full bg-blue-50 items-center justify-center mr-4`}
>
<MaterialIcons name="inventory" size={20} color="#2563eb" />
</View>
<View>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
{selectedOrder?.isPackaged
? "Unmark Packaged"
: "Mark Packaged"}
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
{selectedOrder?.isPackaged
? "Revert to not packaged"
: "Update status to packaged"}
</MyText>
</View>
<MaterialIcons
name="chevron-right"
size={24}
color="#9ca3af"
style={tw`ml-auto`}
/>
</TouchableOpacity>
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm ${
updateDeliveredMutation.isPending ? "opacity-50" : ""
}`}
onPress={async () => {
if (!selectedOrder) return; if (!selectedOrder) return;
try { updateDeliveredMutation.mutate(
await updateDeliveredMutation.mutateAsync({ { orderId: selectedOrder.id.toString(), isDelivered: !selectedOrder.isDelivered },
orderId: selectedOrder.id.toString(), {
isDelivered: !selectedOrder.isDelivered, onSuccess: () => {
});
refetchOrders(); refetchOrders();
refetchSequence(); refetchSequence();
setShowOrderMenu(false); },
} catch (error) { onError: () => {
Alert.alert("Error", "Failed to update delivered status"); Alert.alert("Error", "Failed to update delivered status");
},
} }
}}
disabled={updateDeliveredMutation.isPending}
>
<View
style={tw`w-10 h-10 rounded-full bg-green-50 items-center justify-center mr-4`}
>
<MaterialIcons name="local-shipping" size={20} color="#16a34a" />
</View>
<View>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
{selectedOrder?.isDelivered
? "Unmark Delivered"
: "Mark Delivered"}
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
{selectedOrder?.isDelivered
? "Revert delivery status"
: "Complete the delivery"}
</MyText>
</View>
<MaterialIcons
name="chevron-right"
size={24}
color="#9ca3af"
style={tw`ml-auto`}
/>
</TouchableOpacity>
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm ${
updateAddressCoordsMutation.isPending ? "opacity-50" : ""
}`}
onPress={async () => {
if (!selectedOrder) return;
try {
const { status } =
await Location.requestForegroundPermissionsAsync();
if (status !== "granted") {
Alert.alert(
"Permission Denied",
"Location permission is required to attach coordinates."
); );
return;
}
const location = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High,
});
const { latitude, longitude } = location.coords;
await updateAddressCoordsMutation.mutateAsync({
addressId: selectedOrder.addressId,
latitude,
longitude,
});
Alert.alert(
"Success",
"Location attached to address successfully."
);
} catch (error) {
Alert.alert(
"Error",
"Failed to attach location. Please try again."
);
}
setShowOrderMenu(false);
}} }}
> onOpenAdminNotes={() => {
<View setShowOrderMenu(false);
style={tw`w-10 h-10 rounded-full bg-orange-50 items-center justify-center mr-4`} setNotesDialogOpen(true);
> }}
<MaterialIcons onCancelOrder={() => {
name="add-location-alt" setShowOrderMenu(false);
size={20} setCancelDialogOpen(true);
color="#ea580c" }}
onAttachLocation={() => {}}
onWhatsApp={() => {}}
onDial={() => {}}
/> />
</View>
<View>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
Attach Location
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
Save coordinates to address
</MyText>
</View>
<MaterialIcons
name="chevron-right"
size={24}
color="#9ca3af"
style={tw`ml-auto`}
/>
</TouchableOpacity>
<TouchableOpacity <CancelOrderDialog
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm`} orderId={selectedOrder?.id || 0}
onPress={() => { open={cancelDialogOpen}
const phoneMatch = selectedOrder?.address.match(/Phone: (\d+)/); onClose={() => setCancelDialogOpen(false)}
const phone = phoneMatch ? phoneMatch[1] : null; onSuccess={() => {
if (phone) { refetchOrders();
Linking.openURL(`whatsapp://send?phone=+91${phone}`); refetchSequence();
} else {
Alert.alert("No phone number found");
}
setShowOrderMenu(false);
}} }}
>
<View
style={tw`w-10 h-10 rounded-full bg-green-50 items-center justify-center mr-4`}
>
<MaterialIcons name="message" size={20} color="#16a34a" />
</View>
<View>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
Message On WhatsApp
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
Send message via WhatsApp
</MyText>
</View>
<MaterialIcons
name="chevron-right"
size={24}
color="#9ca3af"
style={tw`ml-auto`}
/> />
</TouchableOpacity>
<TouchableOpacity <BottomDialog open={notesDialogOpen} onClose={() => setNotesDialogOpen(false)}>
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm`} <OrderNotesForm
onPress={() => { orderId={selectedOrder?.id || 0}
const phoneMatch = selectedOrder?.address.match(/Phone: (\d+)/); initialNotes={selectedOrder?.adminNotes || ''}
const phone = phoneMatch ? phoneMatch[1] : null; onSuccess={() => {
if (phone) { refetchOrders();
Linking.openURL(`tel:${phone}`); refetchSequence();
} else { setNotesDialogOpen(false);
Alert.alert("No phone number found");
}
setShowOrderMenu(false);
}} }}
> onCancel={() => setNotesDialogOpen(false)}
<View
style={tw`w-10 h-10 rounded-full bg-green-50 items-center justify-center mr-4`}
>
<MaterialIcons name="phone" size={20} color="#16a34a" />
</View>
<View>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
Dial Mobile Number
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
Call customer directly
</MyText>
</View>
<MaterialIcons
name="chevron-right"
size={24}
color="#9ca3af"
style={tw`ml-auto`}
/> />
</TouchableOpacity>
</View>
</BottomDialog> </BottomDialog>
</View> </View>
); );

View file

@ -1,5 +1,5 @@
import React, { useState , useEffect } from 'react'; import React, { useState , useEffect } from 'react';
import { View, TouchableOpacity, Alert, TextInput, ActivityIndicator } from 'react-native'; import { View, TouchableOpacity, Alert, TextInput, ActivityIndicator, Linking } from 'react-native';
import { AppContainer, MyText, tw, MyFlatList, BottomDialog, BottomDropdown, Checkbox, theme, MyTextInput } from 'common-ui'; import { AppContainer, MyText, tw, MyFlatList, BottomDialog, BottomDropdown, Checkbox, theme, MyTextInput } from 'common-ui';
import { trpc } from '../../../src/trpc-client'; import { trpc } from '../../../src/trpc-client';
import { useRouter, useLocalSearchParams } from 'expo-router'; import { useRouter, useLocalSearchParams } from 'expo-router';
@ -7,6 +7,8 @@ import dayjs from 'dayjs';
import MaterialIcons from '@expo/vector-icons/MaterialIcons'; import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { Entypo } from '@expo/vector-icons'; import { Entypo } from '@expo/vector-icons';
import CancelOrderDialog from '@/components/CancelOrderDialog'; import CancelOrderDialog from '@/components/CancelOrderDialog';
import { OrderOptionsMenu } from '@/components/OrderOptionsMenu';
import * as Location from 'expo-location';
const AdminNotesForm = ({ orderId, existingNotes, onClose, refetch }: { orderId: string; existingNotes?: string | null; onClose: () => void; refetch: () => void }) => { const AdminNotesForm = ({ orderId, existingNotes, onClose, refetch }: { orderId: string; existingNotes?: string | null; onClose: () => void; refetch: () => void }) => {
const [notesText, setNotesText] = useState(existingNotes || ''); const [notesText, setNotesText] = useState(existingNotes || '');
@ -111,7 +113,6 @@ const OrderItem = ({ order, refetch }: { order: OrderType; refetch: () => void }
{ orderId: order.orderId.toString(), isPackaged }, { orderId: order.orderId.toString(), isPackaged },
{ {
onSuccess: () => { onSuccess: () => {
setMenuOpen(false);
refetch(); refetch();
}, },
} }
@ -123,7 +124,6 @@ const OrderItem = ({ order, refetch }: { order: OrderType; refetch: () => void }
{ orderId: order.orderId.toString(), isDelivered }, { orderId: order.orderId.toString(), isDelivered },
{ {
onSuccess: () => { onSuccess: () => {
setMenuOpen(false);
refetch(); refetch();
}, },
} }
@ -344,79 +344,38 @@ const OrderItem = ({ order, refetch }: { order: OrderType; refetch: () => void }
</View> </View>
</TouchableOpacity> </TouchableOpacity>
<BottomDialog open={menuOpen} onClose={() => setMenuOpen(false)}> <OrderOptionsMenu
<View style={tw`p-6`}> open={menuOpen}
<MyText style={tw`text-lg font-bold text-gray-800 mb-4`}> onClose={() => setMenuOpen(false)}
Order Options order={{
</MyText> id: order.id,
{order.isFlashDelivery && ( readableId: order.readableId,
<View style={tw`bg-amber-50 border border-amber-200 rounded-lg p-3 mb-4 flex-row items-center`}> isPackaged: order.isPackaged,
<MaterialIcons name="bolt" size={20} color="#D97706" /> isDelivered: order.isDelivered,
<View style={tw`ml-3 flex-1`}> isFlashDelivery: order.isFlashDelivery,
<MyText style={tw`text-sm font-bold text-amber-900`}>Flash Delivery Order</MyText> address: order.address,
<MyText style={tw`text-xs text-amber-700`}> addressId: 0,
Deliver within 30 minutes High Priority adminNotes: order.adminNotes,
</MyText> userNotes: order.userNotes,
</View> latitude: null,
</View> longitude: null,
)} status: order.status,
<TouchableOpacity }}
style={tw`flex-row items-center p-4 bg-gray-50 rounded-lg mb-3`} onViewDetails={handleMenuOption}
onPress={() => handleMarkPackaged(!order.isPackaged)} onTogglePackaged={() => handleMarkPackaged(!order.isPackaged)}
> onToggleDelivered={() => handleMarkDelivered(!order.isDelivered)}
<Entypo name="box" size={20} color="#6B7280" /> onOpenAdminNotes={() => {
<MyText style={tw`text-gray-800 font-medium ml-3`}>
{order.isPackaged ? 'Mark Not Packaged' : 'Mark Packaged'}
</MyText>
</TouchableOpacity>
{order.isPackaged && (
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-gray-50 rounded-lg mb-3`}
onPress={() => handleMarkDelivered(!order.isDelivered)}
>
<Entypo name="location" size={20} color="#6B7280" />
<MyText style={tw`text-gray-800 font-medium ml-3`}>
{order.isDelivered ? 'Mark Not Delivered' : 'Mark Delivered'}
</MyText>
</TouchableOpacity>
)}
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-gray-50 rounded-lg mb-3`}
onPress={handleMenuOption}
>
<Entypo name="info-with-circle" size={20} color="#6B7280" />
<MyText style={tw`text-gray-800 font-medium ml-3`}>
Order Details
</MyText>
</TouchableOpacity>
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-gray-50 rounded-lg mb-3`}
onPress={() => {
setMenuOpen(false); setMenuOpen(false);
setNotesDialogOpen(true); setNotesDialogOpen(true);
}} }}
> onCancelOrder={() => {
<Entypo name="edit" size={20} color="#6B7280" />
<MyText style={tw`text-gray-800 font-medium ml-3`}>
Admin Notes
</MyText>
</TouchableOpacity>
{order.status !== 'cancelled' && (
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-red-50 rounded-lg`}
onPress={() => {
setMenuOpen(false); setMenuOpen(false);
setCancelDialogOpen(true); setCancelDialogOpen(true);
}} }}
> onAttachLocation={() => {}}
<MaterialIcons name="cancel" size={20} color="#DC2626" /> onWhatsApp={() => {}}
<MyText style={tw`text-red-700 font-medium ml-3`}> onDial={() => {}}
Cancel Order />
</MyText>
</TouchableOpacity>
)}
</View>
</BottomDialog>
<BottomDialog open={itemsDialogOpen} onClose={() => setItemsDialogOpen(false)}> <BottomDialog open={itemsDialogOpen} onClose={() => setItemsDialogOpen(false)}>
<View style={tw`py-6`}> <View style={tw`py-6`}>

View file

@ -0,0 +1,306 @@
import React from 'react';
import { View, TouchableOpacity, Linking, Alert, TextInput, ScrollView, Dimensions } from 'react-native';
import * as Location from 'expo-location';
const { height: SCREEN_HEIGHT } = Dimensions.get('window');
import {
MyText,
tw,
BottomDialog,
} from 'common-ui';
import { trpc } from '@/src/trpc-client';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import Ionicons from '@expo/vector-icons/Ionicons';
interface OrderOptionsMenuProps {
open: boolean;
onClose: () => void;
order: {
id: number;
readableId: number;
isPackaged: boolean;
isDelivered: boolean;
isFlashDelivery?: boolean;
address: string;
addressId: number;
adminNotes?: string | null;
userNotes?: string | null;
latitude?: number | null;
longitude?: number | null;
status?: string;
};
onViewDetails: () => void;
onTogglePackaged: () => void;
onToggleDelivered: () => void;
onOpenAdminNotes: () => void;
onCancelOrder: () => void;
onAttachLocation: () => void;
onWhatsApp: () => void;
onDial: () => void;
}
export function OrderOptionsMenu({
open,
onClose,
order,
onViewDetails,
onTogglePackaged,
onToggleDelivered,
onOpenAdminNotes,
onCancelOrder,
onAttachLocation,
onWhatsApp,
onDial,
}: OrderOptionsMenuProps) {
const updateAddressCoordsMutation = trpc.admin.order.updateAddressCoords.useMutation();
const handleAttachLocation = async () => {
try {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
Alert.alert(
'Permission Denied',
'Location permission is required to attach coordinates.'
);
return;
}
const location = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High,
});
const { latitude, longitude } = location.coords;
await updateAddressCoordsMutation.mutateAsync({
addressId: order.addressId,
latitude,
longitude,
});
Alert.alert('Success', 'Location attached to address successfully.');
onAttachLocation();
} catch (error) {
Alert.alert('Error', 'Failed to attach location. Please try again.');
}
};
const extractPhone = (address: string) => {
const phoneMatch = address.match(/Phone: (\d+)/);
return phoneMatch ? phoneMatch[1] : null;
};
const handleWhatsApp = () => {
const phone = extractPhone(order.address);
if (phone) {
Linking.openURL(`whatsapp://send?phone=+91${phone}`);
} else {
Alert.alert('No phone number found');
}
};
const handleDial = () => {
const phone = extractPhone(order.address);
if (phone) {
Linking.openURL(`tel:${phone}`);
} else {
Alert.alert('No phone number found');
}
};
return (
<BottomDialog open={open} onClose={onClose}>
<View style={{ maxHeight: SCREEN_HEIGHT * 0.7 }}>
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{ flexGrow: 1 }}>
<View style={tw`pb-8 pt-2 px-4`}>
<View style={tw`items-center mb-6`}>
<View style={tw`w-12 h-1.5 bg-gray-200 rounded-full mb-4`} />
<MyText style={tw`text-lg font-bold text-gray-900`}>
Order #{order.readableId}
</MyText>
<MyText style={tw`text-sm text-gray-500`}>
Select an action to perform
</MyText>
</View>
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm`}
onPress={() => {
onViewDetails();
onClose();
}}
>
<View style={tw`w-10 h-10 rounded-full bg-purple-50 items-center justify-center mr-4`}>
<MaterialIcons name="visibility" size={20} color="#9333ea" />
</View>
<View>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
View Details
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
See full order information
</MyText>
</View>
<MaterialIcons name="chevron-right" size={24} color="#9ca3af" style={tw`ml-auto`} />
</TouchableOpacity>
<View style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm`}>
<View
style={tw`p-1`}
hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
onStartShouldSetResponder={() => true}
onResponderEnd={(e) => {
e.stopPropagation();
onTogglePackaged();
}}
>
<Ionicons
name={order.isPackaged ? 'checkbox' : 'square-outline'}
size={24}
color={order.isPackaged ? '#10B981' : '#1570EF'}
/>
</View>
<View style={tw`ml-3 flex-1`}>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
Packaged
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
{order.isPackaged ? 'Mark as not packaged' : 'Mark as packaged'}
</MyText>
</View>
</View>
<View style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm`}>
<View
style={tw`p-1`}
hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
onStartShouldSetResponder={() => true}
onResponderEnd={(e) => {
e.stopPropagation();
onToggleDelivered();
}}
>
<Ionicons
name={order.isDelivered ? 'checkbox' : 'square-outline'}
size={24}
color={order.isDelivered ? '#10B981' : '#1570EF'}
/>
</View>
<View style={tw`ml-3 flex-1`}>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
Delivered
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
{order.isDelivered ? 'Mark as not delivered' : 'Mark as delivered'}
</MyText>
</View>
</View>
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm`}
onPress={() => {
onOpenAdminNotes();
onClose();
}}
>
<View style={tw`w-10 h-10 rounded-full bg-yellow-50 items-center justify-center mr-4`}>
<MaterialIcons name="edit" size={20} color="#ca8a04" />
</View>
<View>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
Admin Notes
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
{order.adminNotes ? 'Edit existing notes' : 'Add admin notes'}
</MyText>
</View>
<MaterialIcons name="chevron-right" size={24} color="#9ca3af" style={tw`ml-auto`} />
</TouchableOpacity>
{order.status !== 'cancelled' && (
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm`}
onPress={() => {
onCancelOrder();
onClose();
}}
>
<View style={tw`w-10 h-10 rounded-full bg-red-50 items-center justify-center mr-4`}>
<MaterialIcons name="cancel" size={20} color="#dc2626" />
</View>
<View>
<MyText style={tw`font-semibold text-red-700 text-base`}>
Cancel Order
</MyText>
<MyText style={tw`text-red-500 text-xs`}>
Cancel and provide reason
</MyText>
</View>
<MaterialIcons name="chevron-right" size={24} color="#9ca3af" style={tw`ml-auto`} />
</TouchableOpacity>
)}
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm`}
onPress={() => {
handleAttachLocation();
onClose();
}}
disabled={updateAddressCoordsMutation.isPending}
>
<View style={tw`w-10 h-10 rounded-full bg-orange-50 items-center justify-center mr-4`}>
<MaterialIcons name="add-location-alt" size={20} color="#ea580c" />
</View>
<View>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
Attach Location
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
Save GPS coordinates to address
</MyText>
</View>
<MaterialIcons name="chevron-right" size={24} color="#9ca3af" style={tw`ml-auto`} />
</TouchableOpacity>
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm`}
onPress={() => {
handleWhatsApp();
onClose();
}}
>
<View style={tw`w-10 h-10 rounded-full bg-green-50 items-center justify-center mr-4`}>
<MaterialIcons name="message" size={20} color="#16a34a" />
</View>
<View>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
Message On WhatsApp
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
Send message via WhatsApp
</MyText>
</View>
<MaterialIcons name="chevron-right" size={24} color="#9ca3af" style={tw`ml-auto`} />
</TouchableOpacity>
<TouchableOpacity
style={tw`flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm`}
onPress={() => {
handleDial();
onClose();
}}
>
<View style={tw`w-10 h-10 rounded-full bg-green-50 items-center justify-center mr-4`}>
<MaterialIcons name="phone" size={20} color="#16a34a" />
</View>
<View>
<MyText style={tw`font-semibold text-gray-800 text-base`}>
Dial Mobile Number
</MyText>
<MyText style={tw`text-gray-500 text-xs`}>
Call customer directly
</MyText>
</View>
<MaterialIcons name="chevron-right" size={24} color="#9ca3af" style={tw`ml-auto`} />
</TouchableOpacity>
</View>
</ScrollView>
</View>
</BottomDialog>
);
}

View file

@ -0,0 +1,2 @@
ALTER TABLE "mf"."addresses" ADD COLUMN "admin_latitude" real;--> statement-breakpoint
ALTER TABLE "mf"."addresses" ADD COLUMN "admin_longitude" real;

File diff suppressed because it is too large Load diff

View file

@ -477,6 +477,13 @@
"when": 1769280779210, "when": 1769280779210,
"tag": "0067_messy_earthquake", "tag": "0067_messy_earthquake",
"breakpoints": true "breakpoints": true
},
{
"idx": 68,
"version": "7",
"when": 1769709890336,
"tag": "0068_colossal_magma",
"breakpoints": true
} }
] ]
} }

View file

@ -48,6 +48,8 @@ export const addresses = mf.table('addresses', {
isDefault: boolean('is_default').notNull().default(false), isDefault: boolean('is_default').notNull().default(false),
latitude: real('latitude'), latitude: real('latitude'),
longitude: real('longitude'), longitude: real('longitude'),
adminLatitude: real('admin_latitude'),
adminLongitude: real('admin_longitude'),
zoneId: integer('zone_id').references(() => addressZones.id), zoneId: integer('zone_id').references(() => addressZones.id),
createdAt: timestamp('created_at').notNull().defaultNow(), createdAt: timestamp('created_at').notNull().defaultNow(),
}); });

View file

@ -520,8 +520,8 @@ export const orderRouter = router({
order.address.pincode order.address.pincode
}, Phone: ${order.address.phone}`, }, Phone: ${order.address.phone}`,
addressId: order.addressId, addressId: order.addressId,
latitude: order.address.latitude, latitude: order.address.adminLatitude ?? order.address.latitude,
longitude: order.address.longitude, longitude: order.address.adminLongitude ?? order.address.longitude,
totalAmount: parseFloat(order.totalAmount), totalAmount: parseFloat(order.totalAmount),
items, items,
deliveryTime: order.slot?.deliveryTime.toISOString() || null, deliveryTime: order.slot?.deliveryTime.toISOString() || null,
@ -646,8 +646,8 @@ export const orderRouter = router({
const result = await db const result = await db
.update(addresses) .update(addresses)
.set({ .set({
latitude, adminLatitude: latitude,
longitude, adminLongitude: longitude,
}) })
.where(eq(addresses.id, addressId)) .where(eq(addresses.id, addressId))
.returning(); .returning();
@ -794,8 +794,8 @@ export const orderRouter = router({
order.address.pincode order.address.pincode
}, Phone: ${order.address.phone}`, }, Phone: ${order.address.phone}`,
addressId: order.addressId, addressId: order.addressId,
latitude: order.address.latitude, latitude: order.address.adminLatitude ?? order.address.latitude,
longitude: order.address.longitude, longitude: order.address.adminLongitude ?? order.address.longitude,
totalAmount: parseFloat(order.totalAmount), totalAmount: parseFloat(order.totalAmount),
deliveryCharge: parseFloat(order.deliveryCharge || "0"), deliveryCharge: parseFloat(order.deliveryCharge || "0"),
items, items,

View file

@ -63,8 +63,8 @@ const isDevMode = Constants.executionEnvironment !== "standalone";
// const BASE_API_URL = API_URL; // const BASE_API_URL = API_URL;
// const BASE_API_URL = 'http://10.0.2.2:4000'; // const BASE_API_URL = 'http://10.0.2.2:4000';
// const BASE_API_URL = 'http://192.168.100.101:4000'; // const BASE_API_URL = 'http://192.168.100.101:4000';
// const BASE_API_URL = 'http://192.168.1.14:4000'; const BASE_API_URL = 'http://192.168.1.14:4000';
let BASE_API_URL = "https://mf.freshyo.in"; // let BASE_API_URL = "https://mf.freshyo.in";
// let BASE_API_URL = 'http://192.168.100.103:4000'; // let BASE_API_URL = 'http://192.168.100.103:4000';
// let BASE_API_URL = 'http://192.168.29.219:4000'; // let BASE_API_URL = 'http://192.168.29.219:4000';