This commit is contained in:
shafi54 2026-02-21 19:42:18 +05:30
parent a875e63751
commit 04ea8c9284
6 changed files with 110 additions and 22 deletions

View file

@ -0,0 +1 @@
This is a demo file.

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -163,6 +163,15 @@ if (fs.existsSync(fallbackUiIndex)) {
console.warn(`Fallback UI build not found at ${fallbackUiIndex}`)
}
// Serve /assets/public folder at /assets route
const assetsPublicDir = path.resolve(__dirname, './assets/public');
if (fs.existsSync(assetsPublicDir)) {
app.use('/assets', express.static(assetsPublicDir));
console.log('Serving /assets from', assetsPublicDir);
} else {
console.warn('Assets public folder not found at', assetsPublicDir);
}
// Global error handler
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
console.error(err);

View file

@ -1,10 +1,12 @@
import React, { useState, useEffect, useRef } from 'react';
import { View, Text, TouchableOpacity, ScrollView } from 'react-native';
import { View, Text, TouchableOpacity, ScrollView, Alert } from 'react-native';
import { tw, BottomDialog, RawBottomDialog } from 'common-ui';
import { useQueryClient } from '@tanstack/react-query';
import AddressForm from '@/src/components/AddressForm';
import LocationAttacher from '@/src/components/LocationAttacher';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { trpc } from '@/src/trpc-client';
import * as Location from 'expo-location';
interface AddressSelectorProps {
selectedAddress: number | null;
@ -16,10 +18,24 @@ const CheckoutAddressSelector: React.FC<AddressSelectorProps> = ({
onAddressSelect,
}) => {
const [showAddAddress, setShowAddAddress] = useState(false);
const [editingLocationAddressId, setEditingLocationAddressId] = useState<number | null>(null);
const [locationLoading, setLocationLoading] = useState(false);
const queryClient = useQueryClient();
const scrollViewRef = useRef<ScrollView>(null);
const { data: addresses } = trpc.user.address.getUserAddresses.useQuery();
const updateAddressMutation = trpc.user.address.updateAddress.useMutation({
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['user.address.getUserAddresses'] });
setEditingLocationAddressId(null);
Alert.alert('Success', 'Location attached successfully');
},
onError: (error: any) => {
setEditingLocationAddressId(null);
Alert.alert('Error', error.message || 'Failed to attach location');
},
});
// Sort addresses with selected first, then default, then others
const sortedAddresses = React.useMemo(() => {
if (!addresses?.data) return [];
@ -52,6 +68,45 @@ const CheckoutAddressSelector: React.FC<AddressSelectorProps> = ({
scrollViewRef.current?.scrollTo({ x: 0, y: 0, animated: true });
};
const handleAttachLocation = async (address: any) => {
setEditingLocationAddressId(address.id);
setLocationLoading(true);
try {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
Alert.alert('Permission Denied', 'Location permission is required to attach your current location');
setLocationLoading(false);
setEditingLocationAddressId(null);
return;
}
const location = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High,
});
const { latitude, longitude } = location.coords;
updateAddressMutation.mutate({
id: address.id,
name: address.name,
phone: address.phone,
addressLine1: address.addressLine1,
addressLine2: address.addressLine2 || '',
city: address.city,
state: address.state,
pincode: address.pincode,
isDefault: address.isDefault,
latitude,
longitude,
});
} catch (error) {
Alert.alert('Error', 'Unable to fetch location. Please check your GPS settings.');
setLocationLoading(false);
setEditingLocationAddressId(null);
}
};
return (
<>
<View style={tw`bg-white p-5 rounded-2xl shadow-sm mb-4 border border-gray-100`}>
@ -126,8 +181,25 @@ const CheckoutAddressSelector: React.FC<AddressSelectorProps> = ({
))}
</ScrollView>
)}
{/* Attach Location for selected address - outside the white box */}
{selectedAddress && (() => {
const selectedAddr = sortedAddresses.find(a => a.id === selectedAddress);
return selectedAddr && !selectedAddr.latitude && !selectedAddr.longitude ? (
<TouchableOpacity
onPress={() => handleAttachLocation(selectedAddr)}
disabled={locationLoading && editingLocationAddressId === selectedAddr.id}
style={tw`mt-3 py-2 px-3 bg-blue-50 rounded-lg self-start`}
>
<Text style={tw`text-blue-600 text-sm font-bold`}>
{locationLoading && editingLocationAddressId === selectedAddr.id ? 'Attaching...' : '+ Attach Current Location'}
</Text>
</TouchableOpacity>
) : null;
})()}
</View>
{/* <BottomDialog open={showAddAddress} onClose={() => setShowAddAddress(false)}> */}
<RawBottomDialog open={showAddAddress} onClose={() => setShowAddAddress(false)}>

View file

@ -1,5 +1,5 @@
import React from 'react';
import { View, TouchableOpacity, ActivityIndicator } from 'react-native';
import { View, TouchableOpacity, ActivityIndicator, Linking } from 'react-native';
import { useRouter } from 'expo-router';
import { tw, MyText } from 'common-ui';
import { useNavigationTarget } from 'common-ui/hooks/useNavigationTarget';
@ -8,6 +8,7 @@ import dayjs from 'dayjs';
import { trpc } from '@/src/trpc-client';
import { Image } from 'expo-image';
import { orderStatusManipulator } from '@/src/lib/string-manipulators';
import { useGetEssentialConsts } from '@/src/api-hooks/essential-consts.api';
interface OrderItem {
productName: string;
@ -47,23 +48,15 @@ export default function NextOrderGlimpse() {
pageSize: 50,
});
const allOrders: Order[] = ordersData?.data || [];
const { data: essentialConsts } = useGetEssentialConsts();
const now = dayjs();
const allOrders: Order[] = ordersData?.data || [];
const upcomingOrders = allOrders.filter(order => {
if (order.orderStatus.toLowerCase() === 'cancelled') return false;
if (order.deliveryStatus.toLowerCase() === 'success') return false;
if (order.isFlashDelivery) {
return true;
}
if (order.deliveryDate) {
return dayjs(order.deliveryDate).isAfter(now);
}
return false;
});
upcomingOrders.sort((a, b) => {
@ -177,6 +170,19 @@ export default function NextOrderGlimpse() {
<Ionicons name="chevron-forward" size={14} color="#D97706" style={tw`ml-auto`} />
</View>
)}
{/* Support Mobile Number */}
{essentialConsts?.supportMobile && (
<TouchableOpacity
style={tw`flex-row items-center mt-3 pt-3 border-t border-amber-200`}
onPress={() => Linking.openURL(`tel:${essentialConsts.supportMobile}`)}
>
<MaterialIcons name="phone-in-talk" size={14} color="#D97706" style={tw`mr-1.5`} />
<MyText style={tw`text-xs text-amber-700 font-medium`}>
Need help? Call {essentialConsts.supportMobile}
</MyText>
</TouchableOpacity>
)}
</View>
</TouchableOpacity>
);