669 lines
No EOL
25 KiB
TypeScript
Executable file
669 lines
No EOL
25 KiB
TypeScript
Executable file
import React, { useState, useCallback, useEffect } from 'react';
|
|
import { View, FlatList, Alert, ActivityIndicator } from 'react-native';
|
|
import { Image } from 'expo-image';
|
|
import { MaterialIcons, Ionicons } from '@expo/vector-icons';
|
|
import { useRouter } from 'expo-router';
|
|
import { tw, useManualRefresh, MyText, MyFlatList, useMarkDataFetchers, REFUND_STATUS, MyTouchableOpacity, theme } from 'common-ui';
|
|
|
|
import { trpc } from '@/src/trpc-client';
|
|
// import RazorpayCheckout from 'react-native-razorpay';
|
|
import OrderMenu from '@/components/OrderMenu';
|
|
import dayjs from 'dayjs';
|
|
|
|
// Type definitions
|
|
interface OrderItem {
|
|
productName: string;
|
|
quantity: number;
|
|
price: number;
|
|
amount: number;
|
|
image: string | null;
|
|
}
|
|
|
|
interface Order {
|
|
id: number;
|
|
orderId: string;
|
|
orderDate: string;
|
|
deliveryStatus: string;
|
|
deliveryDate?: string;
|
|
orderStatus: string;
|
|
cancelReason: string | null;
|
|
totalAmount: number;
|
|
deliveryCharge: number;
|
|
paymentMode: string;
|
|
paymentStatus: string;
|
|
refundStatus: string;
|
|
refundAmount: number | null;
|
|
userNotes: string | null;
|
|
items: OrderItem[];
|
|
discountAmount?: number;
|
|
isFlashDelivery: boolean;
|
|
createdAt: string;
|
|
}
|
|
|
|
interface OrderFooterProps {
|
|
isLoadingMore: boolean;
|
|
loadMoreError: string | null;
|
|
hasNextPage: boolean;
|
|
allOrders: Order[];
|
|
onRetryLoadMore: () => void;
|
|
}
|
|
|
|
const OrderFooter: React.FC<OrderFooterProps> = ({
|
|
isLoadingMore,
|
|
loadMoreError,
|
|
hasNextPage,
|
|
allOrders,
|
|
onRetryLoadMore
|
|
}) => {
|
|
if (isLoadingMore) {
|
|
return (
|
|
<View style={tw`py-6 items-center`}>
|
|
<ActivityIndicator size="small" color="#3B82F6" />
|
|
<MyText style={tw`text-gray-500 mt-2 text-sm`}>Loading more orders...</MyText>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
if (loadMoreError) {
|
|
return (
|
|
<View style={tw`py-6 items-center`}>
|
|
<MyText style={tw`text-red-500 text-sm mb-2`}>{loadMoreError}</MyText>
|
|
<MyTouchableOpacity
|
|
onPress={onRetryLoadMore}
|
|
style={tw`bg-blue-500 px-4 py-2 rounded-lg`}
|
|
>
|
|
<MyText style={tw`text-white font-medium text-sm`}>Retry</MyText>
|
|
</MyTouchableOpacity>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
if (!hasNextPage && allOrders.length > 0) {
|
|
return (
|
|
<View style={tw`py-6 items-center`}>
|
|
<MyText style={tw`text-gray-400 text-xs`}>End of list</MyText>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
interface OrderItemProps {
|
|
item: Order;
|
|
index: number;
|
|
getStatusColor: (status: string) => any;
|
|
getRefundStatusColor: (status: string) => any;
|
|
onPress: (orderId: number) => void;
|
|
onRetryPayment: (orderId: number) => void;
|
|
onViewMoreItems: (orderId: number) => void;
|
|
isPaymentPending: boolean;
|
|
}
|
|
|
|
const OrderItem: React.FC<OrderItemProps> = ({
|
|
item,
|
|
index,
|
|
getStatusColor,
|
|
getRefundStatusColor,
|
|
onPress,
|
|
onRetryPayment,
|
|
onViewMoreItems,
|
|
isPaymentPending
|
|
}) => {
|
|
const mainStatus = getStatusColor(item.orderStatus);
|
|
const deliveryStatus = getStatusColor(item.deliveryStatus);
|
|
const totalAmount = item.totalAmount;
|
|
|
|
return (
|
|
<MyTouchableOpacity
|
|
style={tw`bg-white rounded-[28px] mb-6 border border-slate-100 shadow-sm overflow-hidden`}
|
|
onPress={() => onPress(item.id)}
|
|
activeOpacity={0.95}
|
|
>
|
|
{/* Top Header: Order ID and Status */}
|
|
<View style={tw`flex-row justify-between items-center px-5 py-4 bg-slate-50/50 border-b border-slate-100`}>
|
|
<View>
|
|
<View style={tw`flex-row items-center`}>
|
|
<View style={tw`w-2 h-2 rounded-full bg-brand500 mr-2`} />
|
|
<MyText style={tw`text-[10px] font-bold text-slate-400 uppercase tracking-tighter`}>Order Reference</MyText>
|
|
</View>
|
|
<MyText style={tw`text-base font-extrabold text-slate-900 mt-0.5`}>#{item.orderId}</MyText>
|
|
</View>
|
|
|
|
<View style={tw`flex-row items-center gap-2`}>
|
|
{item.orderStatus.toLowerCase() === 'cancelled' && (
|
|
<View style={[tw`flex-row items-center px-3 py-1.5 rounded-full border ${mainStatus.bg} ${mainStatus.border}`]}>
|
|
<MaterialIcons name={mainStatus.icon as any} size={14} color={mainStatus.color} />
|
|
<MyText style={[tw`text-[11px] font-bold ml-1.5 uppercase tracking-wide`, { color: mainStatus.color }]}>
|
|
{mainStatus.label}
|
|
</MyText>
|
|
</View>
|
|
)}
|
|
{item.isFlashDelivery && (
|
|
<View style={tw`px-2 py-1 bg-amber-100 rounded-full border border-amber-200 flex-row items-center`}>
|
|
<MyText style={tw`text-[10px] font-black text-amber-700 uppercase mr-1`}>⚡</MyText>
|
|
<MyText style={tw`text-[10px] font-black text-amber-700 uppercase`}>FLASH</MyText>
|
|
</View>
|
|
)}
|
|
</View>
|
|
</View>
|
|
|
|
<View style={tw`p-5`}>
|
|
{/* Order Date & Quick Stats */}
|
|
<View style={tw`flex-row justify-between items-start mb-5`}>
|
|
<View style={tw`flex-col gap-3 flex-1`}>
|
|
{(item.deliveryDate || item.isFlashDelivery) && (
|
|
<View style={tw`flex-row items-center`}>
|
|
<View style={[tw`w-9 h-9 rounded-xl items-center justify-center mr-3`, item.isFlashDelivery ? tw`bg-amber-50` : tw`bg-brand50`]}>
|
|
{item.isFlashDelivery ? (
|
|
<MaterialIcons name="bolt" size={18} color="#D97706" />
|
|
) : (
|
|
<Ionicons name="time" size={18} color={theme.colors.brand600} />
|
|
)}
|
|
</View>
|
|
<View>
|
|
<MyText style={[tw`text-[10px] font-bold uppercase`, item.isFlashDelivery ? tw`text-amber-700` : tw`text-brand700`]}>
|
|
{item.isFlashDelivery ? "Flash Delivery" : "Delivery Time"}
|
|
</MyText>
|
|
<MyText style={[tw`text-sm font-extrabold`, item.isFlashDelivery ? tw`text-amber-900` : tw`text-brand900`]}>
|
|
{item.isFlashDelivery
|
|
? dayjs(item.createdAt || item.orderDate).add(30, 'minutes').format("DD MMM, hh:mm A")
|
|
: dayjs(item.deliveryDate).format("DD MMM, hh:mm A")
|
|
}
|
|
</MyText>
|
|
{item.isFlashDelivery && (
|
|
<View style={tw`flex-row items-center mt-1`}>
|
|
<MyText style={tw`text-[9px] font-bold text-amber-600 uppercase`}>⚡ 30-Min Delivery</MyText>
|
|
</View>
|
|
)}
|
|
</View>
|
|
</View>
|
|
)}
|
|
|
|
<View style={tw`flex-row items-center`}>
|
|
<View style={tw`w-9 h-9 rounded-xl bg-slate-100 items-center justify-center mr-3`}>
|
|
<MaterialIcons name="calendar-today" size={18} color="#64748B" />
|
|
</View>
|
|
<View>
|
|
<MyText style={tw`text-[10px] font-bold text-slate-400 uppercase`}>Placed On</MyText>
|
|
<MyText style={tw`text-sm font-semibold text-slate-800`}>
|
|
{dayjs(item.orderDate).format("DD MMM YYYY, hh:mm A")}
|
|
</MyText>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
<OrderMenu
|
|
orderId={item.id.toString()}
|
|
postActionHandler={() => {
|
|
// refetch will be handled by parent
|
|
}}
|
|
/>
|
|
</View>
|
|
|
|
{/* Items Section */}
|
|
<View style={tw`bg-slate-50/50 rounded-2xl p-3 mb-5 border border-slate-100`}>
|
|
<View style={tw`flex-row justify-between items-center mb-3 px-1`}>
|
|
<MyText style={tw`text-[11px] font-bold text-slate-500 uppercase tracking-wider`}>Items Summary</MyText>
|
|
<MyText style={tw`text-[11px] font-bold text-brand600`}>{item.items.length} {item.items.length === 1 ? 'Item' : 'Items'}</MyText>
|
|
</View>
|
|
|
|
{item.items.slice(0, 2).map((product, index) => (
|
|
<View key={index} style={tw`flex-row items-center mb-3 last:mb-0`}>
|
|
<View style={tw`relative`}>
|
|
<Image
|
|
source={{ uri: product.image || undefined }}
|
|
style={tw`w-12 h-12 rounded-xl bg-white border border-slate-200`}
|
|
defaultSource={require('@/assets/logo.png')}
|
|
/>
|
|
<View style={tw`absolute -top-1.5 -right-1.5 bg-brand500 w-5 h-5 rounded-full items-center justify-center border-2 border-white`}>
|
|
<MyText style={tw`text-[10px] font-bold text-white`}>{product.quantity}</MyText>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={tw`flex-1 ml-4`}>
|
|
<MyText style={tw`text-sm font-bold text-slate-800`} numberOfLines={1}>
|
|
{product.productName}
|
|
</MyText>
|
|
<MyText style={tw`text-xs text-slate-500 mt-0.5 font-medium`}>
|
|
Unit Price: ₹{product.price}
|
|
</MyText>
|
|
</View>
|
|
<MyText style={tw`text-sm font-extrabold text-slate-900`}>
|
|
₹{product.amount}
|
|
</MyText>
|
|
</View>
|
|
))}
|
|
|
|
{item.items.length > 2 && (
|
|
<MyTouchableOpacity
|
|
style={tw`mt-2 pt-2 border-t border-slate-100 items-center`}
|
|
onPress={() => onViewMoreItems(item.id)}
|
|
>
|
|
<MyText style={tw`text-xs text-brand600 font-bold`}>
|
|
+ {item.items.length - 2} more {item.items.length - 2 === 1 ? 'item' : 'items'} in this order
|
|
</MyText>
|
|
</MyTouchableOpacity>
|
|
)}
|
|
</View>
|
|
|
|
{/* Delivery Status - Single Line */}
|
|
<View style={tw`flex-row items-center justify-between mb-5`}>
|
|
<View style={tw`flex-row items-center flex-1`}>
|
|
<View style={[tw`p-1.5 rounded-lg mr-2`, { backgroundColor: deliveryStatus.color + '15' }]}>
|
|
{item.isFlashDelivery ? (
|
|
<MaterialIcons name="bolt" size={14} color="#D97706" />
|
|
) : (
|
|
<MaterialIcons name="local-shipping" size={14} color={deliveryStatus.color} />
|
|
)}
|
|
</View>
|
|
<MyText style={tw`text-[10px] font-bold text-slate-400 uppercase mr-2`}>
|
|
{item.isFlashDelivery ? "Flash Delivery:" : "Shipping Status:"}
|
|
</MyText>
|
|
<MyText style={[tw`text-xs font-bold`, item.isFlashDelivery ? tw`text-amber-700` : { color: deliveryStatus.color }]} numberOfLines={1}>
|
|
{item.deliveryStatus}
|
|
</MyText>
|
|
</View>
|
|
{item.isFlashDelivery && (
|
|
<View style={tw`px-2 py-0.5 bg-amber-100 rounded-full border border-amber-200`}>
|
|
<MyText style={tw`text-[8px] font-black text-amber-700 uppercase`}>⚡ FAST</MyText>
|
|
</View>
|
|
)}
|
|
</View>
|
|
|
|
{/* User Notes or Delivery Time if present */}
|
|
{item.userNotes && (
|
|
<View style={tw`flex-col gap-3 mb-5`}>
|
|
<View style={tw`flex-row items-start bg-amber-50/50 p-3 rounded-2xl border border-amber-100`}>
|
|
<MaterialIcons name="sticky-note-2" size={16} color="#D97706" style={tw`mt-0.5`} />
|
|
<View style={tw`ml-3 flex-1`}>
|
|
<MyText style={tw`text-[10px] font-bold text-amber-700 uppercase`}>Your Instructions</MyText>
|
|
<MyText style={tw`text-xs font-medium text-amber-900 mt-0.5`} numberOfLines={2}>{item.userNotes}</MyText>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
)}
|
|
|
|
{/* Cancellation Info */}
|
|
{item.cancelReason && (
|
|
<View style={tw`bg-rose-50 p-4 rounded-2xl mb-5 border border-rose-100`}>
|
|
<View style={tw`flex-row items-center mb-2`}>
|
|
<Ionicons name="alert-circle" size={16} color="#E11D48" />
|
|
<MyText style={tw`text-xs text-rose-800 font-bold ml-2`}>Cancellation Details</MyText>
|
|
</View>
|
|
<MyText style={tw`text-xs text-rose-600 font-medium leading-relaxed`}>{item.cancelReason}</MyText>
|
|
|
|
{item.refundStatus && item.refundStatus !== REFUND_STATUS.NOT_APPLICABLE && (
|
|
<View style={tw`mt-3 pt-3 border-t border-rose-100 flex-row justify-between items-center`}>
|
|
<MyText style={tw`text-[10px] font-bold text-rose-700 uppercase`}>Refund Status</MyText>
|
|
<View style={tw`flex-row items-center px-2 py-1 bg-white rounded-lg border border-rose-200`}>
|
|
<MaterialIcons
|
|
name={getRefundStatusColor(item.refundStatus).icon as any}
|
|
size={12}
|
|
color={getRefundStatusColor(item.refundStatus).color}
|
|
/>
|
|
<MyText style={[tw`text-[10px] font-bold ml-1.5 uppercase`, { color: getRefundStatusColor(item.refundStatus).color }]}>
|
|
{item.refundStatus}
|
|
</MyText>
|
|
</View>
|
|
</View>
|
|
)}
|
|
</View>
|
|
)}
|
|
|
|
{/* Footer: Price and CTA */}
|
|
<View style={tw`flex-row justify-between items-center pt-5 border-t border-slate-100`}>
|
|
<View>
|
|
<MyText style={tw`text-[10px] font-bold text-slate-400 uppercase tracking-wider`}>Amount to Pay</MyText>
|
|
<MyText style={tw`text-2xl font-black text-slate-900`}>₹{totalAmount}</MyText>
|
|
{item.discountAmount ? (
|
|
<View style={tw`flex-row items-center mt-1 bg-emerald-50 self-start px-2 py-0.5 rounded-full`}>
|
|
<MaterialIcons name="local-offer" size={10} color="#059669" />
|
|
<MyText style={tw`text-[10px] font-bold text-emerald-700 ml-1`}>Saved ₹{item.discountAmount}</MyText>
|
|
</View>
|
|
) : null}
|
|
{item.deliveryCharge > 0 && (
|
|
<View style={tw`flex-row items-center mt-1`}>
|
|
<MaterialIcons name="local-shipping" size={12} color="#6B7280" style={tw`mr-1`} />
|
|
<MyText style={tw`text-[10px] font-medium text-slate-600`}>Delivery: ₹{item.deliveryCharge}</MyText>
|
|
</View>
|
|
)}
|
|
</View>
|
|
|
|
{(item.paymentMode === 'Online' && (item.paymentStatus === 'pending' || item.paymentStatus === 'failed')) ? (
|
|
<MyTouchableOpacity
|
|
onPress={(e) => {
|
|
e.stopPropagation();
|
|
onRetryPayment(item.id);
|
|
}}
|
|
disabled={isPaymentPending}
|
|
style={tw`bg-rose-600 px-6 py-3 rounded-2xl shadow-lg shadow-rose-200 flex-row items-center`}
|
|
>
|
|
{isPaymentPending ? (
|
|
<ActivityIndicator size="small" color="white" />
|
|
) : (
|
|
<>
|
|
<MaterialIcons name="payment" size={18} color="white" />
|
|
<MyText style={tw`text-white font-bold text-sm ml-2`}>Retry Payment</MyText>
|
|
</>
|
|
)}
|
|
</MyTouchableOpacity>
|
|
) : (
|
|
<View style={tw`bg-brand600 px-5 py-3 rounded-2xl shadow-lg shadow-brand100 flex-row items-center`}>
|
|
<MyText style={tw`text-white font-bold text-sm mr-1`}>View Details</MyText>
|
|
<MaterialIcons name="arrow-forward" size={16} color="white" />
|
|
</View>
|
|
)}
|
|
</View>
|
|
</View>
|
|
</MyTouchableOpacity>
|
|
);
|
|
};
|
|
|
|
export default function MyOrders() {
|
|
const router = useRouter();
|
|
|
|
// Infinite scroll state
|
|
const [allOrders, setAllOrders] = useState<Order[]>([]);
|
|
const [currentPage, setCurrentPage] = useState<number>(1);
|
|
const [isLoadingMore, setIsLoadingMore] = useState<boolean>(false);
|
|
const [hasNextPage, setHasNextPage] = useState<boolean>(true);
|
|
const [loadMoreError, setLoadMoreError] = useState<string | null>(null);
|
|
const pageSize = 10;
|
|
|
|
const { data: ordersData, isLoading, error, refetch } = trpc.user.order.getOrders.useQuery({
|
|
page: currentPage,
|
|
pageSize: pageSize,
|
|
});
|
|
|
|
|
|
|
|
const createRazorpayOrderMutation = trpc.user.payment.createRazorpayOrder.useMutation({
|
|
onSuccess: (paymentData) => {
|
|
const order = allOrders.find(o => o.id === retryOrderId);
|
|
if (order) {
|
|
const totalAmount = order.items.reduce((sum, p) => sum + p.amount, 0);
|
|
// initiateRazorpayPayment(paymentData.razorpayOrderId, paymentData.key, totalAmount);
|
|
}
|
|
},
|
|
onError: (error: any) => {
|
|
Alert.alert('Error', error.message || 'Failed to create payment order');
|
|
},
|
|
});
|
|
|
|
const verifyPaymentMutation = trpc.user.payment.verifyPayment.useMutation({
|
|
onSuccess: () => {
|
|
refetch();
|
|
Alert.alert('Success', 'Payment completed successfully');
|
|
},
|
|
onError: (error: any) => {
|
|
Alert.alert('Error', error.message || 'Payment verification failed');
|
|
refetch();
|
|
},
|
|
});
|
|
// Handle data accumulation for infinite scroll
|
|
useEffect(() => {
|
|
if (ordersData?.data) {
|
|
if (currentPage === 1) {
|
|
// First page - replace all orders
|
|
setAllOrders(ordersData.data);
|
|
} else {
|
|
// Subsequent pages - append to existing orders
|
|
setAllOrders(prev => [...prev, ...ordersData.data]);
|
|
}
|
|
|
|
// Check if there are more pages
|
|
const totalPages = ordersData.pagination?.totalPages || 1;
|
|
setHasNextPage(currentPage < totalPages);
|
|
setIsLoadingMore(false);
|
|
setLoadMoreError(null); // Clear any previous errors
|
|
}
|
|
}, [ordersData, currentPage]);
|
|
|
|
// Handle errors during infinite scroll loading
|
|
useEffect(() => {
|
|
if (error && currentPage > 1) {
|
|
// If there's an error loading more pages, show error state
|
|
setIsLoadingMore(false);
|
|
setLoadMoreError('Failed to load more orders. Please try again.');
|
|
}
|
|
}, [error, currentPage]);
|
|
|
|
// Reset to first page on manual refresh
|
|
useManualRefresh(() => {
|
|
setCurrentPage(1);
|
|
setAllOrders([]);
|
|
setHasNextPage(true);
|
|
setIsLoadingMore(false);
|
|
setLoadMoreError(null);
|
|
refetch();
|
|
});
|
|
|
|
useMarkDataFetchers(() => {
|
|
setCurrentPage(1);
|
|
setAllOrders([]);
|
|
setHasNextPage(true);
|
|
setIsLoadingMore(false);
|
|
setLoadMoreError(null);
|
|
refetch();
|
|
});
|
|
|
|
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
|
|
const [dialogItems, setDialogItems] = useState<OrderItem[]>([]);
|
|
const [retryOrderId, setRetryOrderId] = useState<number>(0);
|
|
|
|
const openDialog = useCallback((items: OrderItem[]) => {
|
|
setDialogItems(items);
|
|
setDialogOpen(true);
|
|
}, []);
|
|
|
|
// Infinite scroll functions
|
|
const loadMoreOrders = useCallback(() => {
|
|
if (!isLoadingMore && hasNextPage && !isLoading) {
|
|
setIsLoadingMore(true);
|
|
setCurrentPage(prev => prev + 1);
|
|
}
|
|
}, [isLoadingMore, hasNextPage, isLoading]);
|
|
|
|
|
|
|
|
const getStatusColor = (status: string) => {
|
|
const s = status.toLowerCase();
|
|
switch (s) {
|
|
case 'delivered':
|
|
case 'success':
|
|
case 'completed':
|
|
return {
|
|
bg: 'bg-emerald-50',
|
|
text: 'text-emerald-700',
|
|
icon: 'check-circle',
|
|
color: '#059669',
|
|
border: 'border-emerald-100',
|
|
label: 'Delivered'
|
|
};
|
|
case 'cancelled':
|
|
case 'failed':
|
|
return {
|
|
bg: 'bg-rose-50',
|
|
text: 'text-rose-700',
|
|
icon: 'cancel',
|
|
color: '#E11D48',
|
|
border: 'border-rose-100',
|
|
label: 'Cancelled'
|
|
};
|
|
case 'pending':
|
|
case 'payment_pending':
|
|
return {
|
|
bg: 'bg-amber-50',
|
|
text: 'text-amber-700',
|
|
icon: 'schedule',
|
|
color: '#D97706',
|
|
border: 'border-amber-100',
|
|
label: 'Pending'
|
|
};
|
|
case 'packaged':
|
|
return {
|
|
bg: 'bg-brand50',
|
|
text: 'text-brand700',
|
|
icon: 'inventory',
|
|
color: '#1570EF',
|
|
border: 'border-brand100',
|
|
label: 'Packaged'
|
|
};
|
|
case 'processing':
|
|
case 'confirmed':
|
|
case 'shipped':
|
|
return {
|
|
bg: 'bg-brand50',
|
|
text: 'text-brand700',
|
|
icon: 'local-shipping',
|
|
color: '#1570EF',
|
|
border: 'border-brand100',
|
|
label: status.charAt(0).toUpperCase() + status.slice(1)
|
|
};
|
|
default:
|
|
return {
|
|
bg: 'bg-slate-50',
|
|
text: 'text-slate-700',
|
|
icon: 'info',
|
|
color: '#64748B',
|
|
border: 'border-slate-100',
|
|
label: status
|
|
};
|
|
}
|
|
};
|
|
|
|
const getRefundStatusColor = (status: string) => {
|
|
switch (status) {
|
|
case REFUND_STATUS.SUCCESS:
|
|
return { bg: 'bg-emerald-50', text: 'text-emerald-700', icon: 'check-circle', color: '#059669', border: 'border-emerald-100' };
|
|
case REFUND_STATUS.PROCESSING:
|
|
return { bg: 'bg-brand50', text: 'text-brand700', icon: 'refresh', color: '#1570EF', border: 'border-brand100' };
|
|
case REFUND_STATUS.PENDING:
|
|
return { bg: 'bg-amber-50', text: 'text-amber-700', icon: 'schedule', color: '#D97706', border: 'border-amber-100' };
|
|
case REFUND_STATUS.NOT_APPLICABLE:
|
|
return { bg: 'bg-slate-50', text: 'text-slate-700', icon: 'info', color: '#64748B', border: 'border-slate-100' };
|
|
default:
|
|
return { bg: 'bg-slate-50', text: 'text-slate-700', icon: 'info', color: '#64748B', border: 'border-slate-100' };
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleRetryPayment = (orderId: number) => {
|
|
setRetryOrderId(orderId);
|
|
createRazorpayOrderMutation.mutate({ orderId: orderId.toString() });
|
|
};
|
|
|
|
// const initiateRazorpayPayment = (razorpayOrderId: string, key: string, amount: number) => {
|
|
// const options = {
|
|
// key,
|
|
// amount: amount * 100, // in paisa
|
|
// currency: 'INR',
|
|
// order_id: razorpayOrderId,
|
|
// name: 'Meat Farmer',
|
|
// description: 'Order Payment Retry',
|
|
// prefill: {
|
|
// // Add user details if available
|
|
// },
|
|
// };
|
|
|
|
// RazorpayCheckout.open(options)
|
|
// .then((data: any) => {
|
|
// // Payment success
|
|
// verifyPaymentMutation.mutate({
|
|
// razorpay_payment_id: data.razorpay_payment_id,
|
|
// razorpay_order_id: data.razorpay_order_id,
|
|
// razorpay_signature: data.razorpay_signature,
|
|
// });
|
|
// })
|
|
// .catch((error: any) => {
|
|
// Alert.alert('Payment Failed', 'Payment failed. Please try again.');
|
|
// refetch();
|
|
// });
|
|
// };
|
|
|
|
if (isLoading && currentPage === 1) {
|
|
return (
|
|
<View style={tw`flex-1 justify-center items-center bg-gray-50`}>
|
|
<ActivityIndicator size="large" color="#3B82F6" />
|
|
<MyText style={tw`text-gray-500 mt-4 font-medium`}>Loading your orders...</MyText>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<View style={tw`flex-1 justify-center items-center bg-gray-50 p-6`}>
|
|
<View style={tw`bg-white p-6 rounded-full shadow-sm mb-4`}>
|
|
<MaterialIcons name="error-outline" size={48} color="#EF4444" />
|
|
</View>
|
|
<MyText style={tw`text-gray-900 text-xl font-bold mt-2`}>Unable to load orders</MyText>
|
|
<MyText style={tw`text-gray-500 text-center mt-2 mb-6`}>Please check your connection and try again</MyText>
|
|
<MyTouchableOpacity
|
|
onPress={() => refetch()}
|
|
style={tw`bg-blue-600 px-8 py-3 rounded-full shadow-md`}
|
|
>
|
|
<MyText style={tw`text-white font-bold`}>Retry</MyText>
|
|
</MyTouchableOpacity>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<View style={tw`flex-1 bg-gray-50`}>
|
|
<MyFlatList
|
|
style={tw`flex-1`}
|
|
contentContainerStyle={tw`px-4 py-6`}
|
|
data={allOrders}
|
|
renderItem={({ item, index }) => (
|
|
<OrderItem
|
|
item={item}
|
|
index={index}
|
|
getStatusColor={getStatusColor}
|
|
getRefundStatusColor={getRefundStatusColor}
|
|
onPress={(orderId) => router.push(`/(drawer)/(tabs)/me/my-orders/${orderId}`)}
|
|
onRetryPayment={handleRetryPayment}
|
|
onViewMoreItems={(orderId) => router.push(`/(drawer)/(tabs)/me/my-orders/${orderId}`)}
|
|
isPaymentPending={createRazorpayOrderMutation.isPending}
|
|
/>
|
|
)}
|
|
keyExtractor={(item) => item.orderId}
|
|
showsVerticalScrollIndicator={false}
|
|
onEndReached={loadMoreOrders}
|
|
onEndReachedThreshold={0.5}
|
|
ListFooterComponent={
|
|
<OrderFooter
|
|
isLoadingMore={isLoadingMore}
|
|
loadMoreError={loadMoreError}
|
|
hasNextPage={hasNextPage}
|
|
allOrders={allOrders}
|
|
onRetryLoadMore={() => {
|
|
setLoadMoreError(null);
|
|
loadMoreOrders();
|
|
}}
|
|
/>
|
|
}
|
|
ListEmptyComponent={
|
|
<View style={tw`flex-1 justify-center items-center py-20`}>
|
|
<View style={tw`bg-white p-6 rounded-full shadow-sm mb-4`}>
|
|
<MaterialIcons name="shopping-bag" size={64} color="#E5E7EB" />
|
|
</View>
|
|
<MyText style={tw`text-gray-900 text-lg font-bold mt-2`}>No orders yet</MyText>
|
|
<MyText style={tw`text-gray-500 text-center mt-2`}>Your order history will appear here</MyText>
|
|
<MyTouchableOpacity
|
|
style={tw`mt-6 bg-blue-600 px-6 py-3 rounded-full`}
|
|
onPress={() => router.push('/(drawer)/(tabs)/home')}
|
|
>
|
|
<MyText style={tw`text-white font-bold`}>Start Shopping</MyText>
|
|
</MyTouchableOpacity>
|
|
</View>
|
|
}
|
|
/>
|
|
|
|
|
|
</View>
|
|
);
|
|
} |