import React, { useState } from "react"; import { View, ScrollView, TouchableOpacity, Platform, Alert, } from "react-native"; import { useLocalSearchParams, useRouter } from "expo-router"; import { AppContainer, MyText, tw, BottomDialog, MyTextInput, theme } from "common-ui"; import { trpc } from "@/src/trpc-client"; import MaterialIcons from "@expo/vector-icons/MaterialIcons"; import FontAwesome5 from "@expo/vector-icons/FontAwesome5"; import dayjs from "dayjs"; import CancelOrderDialog from "@/components/CancelOrderDialog"; export default function OrderDetails() { const { id } = useLocalSearchParams<{ id: string }>(); const router = useRouter(); const [generateCouponDialogOpen, setGenerateCouponDialogOpen] = useState(false); const [initiateRefundDialogOpen, setInitiateRefundDialogOpen] = useState(false); const [cancelDialogOpen, setCancelDialogOpen] = useState(false); const [refundType, setRefundType] = useState<"percent" | "amount">("percent"); const [refundValue, setRefundValue] = useState("100"); const [updatingItems, setUpdatingItems] = useState>(new Set()); const { data: orderData, isLoading, error, refetch, } = trpc.admin.order.getOrderDetails.useQuery( { orderId: id ? parseInt(id) : 0 }, { enabled: !!id } ); const generateCouponMutation = trpc.admin.coupon.generateCancellationCoupon.useMutation({ onSuccess: (coupon) => { Alert.alert( "Success", `Refund coupon generated successfully!\n\nCode: ${coupon.couponCode }\nValue: ₹${coupon.flatDiscount}\nExpires: ${coupon.validTill ? new Date(coupon.validTill).toLocaleDateString() : "N/A" }` ); setGenerateCouponDialogOpen(false); }, onError: (error: any) => { Alert.alert( "Error", error.message || "Failed to generate refund coupon" ); }, }); const initiateRefundMutation = trpc.admin.payments.initiateRefund.useMutation( { onSuccess: (result) => { Alert.alert( "Success", `Refund initiated successfully!\n\nAmount: ₹${result.amount}\nStatus: ${result.status}` ); setInitiateRefundDialogOpen(false); }, onError: (error: any) => { Alert.alert("Error", error.message || "Failed to initiate refund"); }, } ); const updateItemPackagingMutation = trpc.admin.order.updateOrderItemPackaging.useMutation({ onSuccess: () => { // Refetch order details to get updated packaging status refetch(); }, onError: (error: any) => { Alert.alert("Error", error.message || "Failed to update packaging status"); }, }); if (isLoading) { return ( Loading order details... ); } if (error || !orderData) { return ( Oops! {error?.message || "Failed to load order details"} router.back()} style={tw`bg-gray-900 px-6 py-3 rounded-xl w-full items-center`} > Go Back ); } const order = orderData; // Calculate subtotal and discount for order summary const subtotal = order.items.reduce((sum, item) => sum + item.amount, 0); const discountAmount = order.discountAmount || 0; const handleGenerateCoupon = () => { generateCouponMutation.mutate({ orderId: order.id }); }; const handleInitiateRefund = () => { const value = parseFloat(refundValue); if (isNaN(value) || value <= 0) { Alert.alert("Error", "Please enter a valid refund value"); return; } const mutationData: any = { orderId: order.id, }; if (refundType === "percent") { if (value > 100) { Alert.alert("Error", "Refund percentage cannot exceed 100%"); return; } mutationData.refundPercent = value; } else { mutationData.refundAmount = value; } initiateRefundMutation.mutate(mutationData); setInitiateRefundDialogOpen(false); }; const handlePackagingToggle = (itemId: number, field: 'isPackaged' | 'isPackageVerified', value: boolean) => { // Add item to updating set to disable UI setUpdatingItems(prev => new Set(prev).add(itemId)); updateItemPackagingMutation.mutate( { orderItemId: itemId, [field]: value, }, { onSettled: () => { // Remove item from updating set setUpdatingItems(prev => { const newSet = new Set(prev); newSet.delete(itemId); return newSet; }); }, } ); }; const getStatusColor = (status: string) => { switch (status) { case "delivered": return "text-green-600 bg-green-50 border-green-100"; case "cancelled": return "text-red-600 bg-red-50 border-red-100"; default: return "text-yellow-600 bg-yellow-50 border-yellow-100"; } }; const statusStyle = getStatusColor(order.status); const showRefundOptions = true; const getRefundDotColor = (status: string) => { if (status === 'success') return 'bg-green-500'; else if (status === 'pending') return 'bg-yellow-500'; else if (status === 'failed') return 'bg-red-500'; else return 'bg-gray-500'; }; const getRefundTextColor = (status: string) => { if (status === 'success' || status === 'na') return 'text-green-700'; else if (status === 'pending') return 'text-yellow-700'; else if (status === 'failed') return 'text-red-700'; else return 'text-gray-700'; }; const getRefundStatusText = (status: string) => { if (status === 'success' || status === 'na') return 'Completed'; else if (status === 'pending') return 'Pending'; else if (status === 'failed') return 'Failed'; else if (status === 'none') return 'Not Initiated'; else if (status === 'na') return 'Not Applicable'; else return 'Unknown'; }; return ( {/* Order ID & Status Card */} Order ID #{order.readableId} {order.status} {order.isFlashDelivery && ( )} {order.isFlashDelivery ? "Flash Delivery:" : "Delivery at:"} {order.isFlashDelivery ? dayjs(order.createdAt).add(30, 'minutes').format("MMM DD, YYYY • h:mm A") : order.slotInfo?.time ? dayjs(order.slotInfo.time).format("MMM DD, YYYY • h:mm A") : 'Not scheduled'} {order.isFlashDelivery && ( ⚡ 30-Minute Flash Delivery • High Priority )} Placed on {dayjs(order.createdAt).format("MMM DD, YYYY • h:mm A")} {/* Order Progress (Simplified Timeline) */} {order.status !== "cancelled" && ( Order Status {/* Placed */} Placed {/* Packaged */} Packaged {/* Delivered */} {order.isFlashDelivery && order.isDelivered ? ( ) : ( )} {order.isFlashDelivery && order.isDelivered ? "Flash Delivered" : "Delivered"} {order.isFlashDelivery && ( ⚡ 30-Min )} )} {/* Customer Details */} Customer Details {order.customerName} Customer {order.customerMobile} {order.customerEmail && ( {order.customerEmail} )} {order.address.line1} {order.address.line2 ? `, ${order.address.line2}` : ""} {`\n${order.address.city}, ${order.address.state} - ${order.address.pincode}`} {/* Order Items */} Items Ordered {order.items.map((item, index) => ( {item.name} {item.quantity} {item.unit} × ₹{item.price} handlePackagingToggle(item.id, 'isPackaged', !item.isPackaged)} disabled={updatingItems.has(item.id)} > Packaged handlePackagingToggle(item.id, 'isPackageVerified', !item.isPackageVerified)} disabled={updatingItems.has(item.id)} > Pkg Verified ₹{item.amount} ))} Subtotal ({order.items.length} items) ₹{subtotal} {discountAmount > 0 && ( Discount -₹{discountAmount} )} Total Amount {order.isFlashDelivery && ( ⚡ FLASH )} ₹{order.totalAmount} {/* Flash Delivery Priority Notice */} {order.isFlashDelivery && ( Flash Delivery Order ⚡ This is a high-priority flash delivery order that must be delivered within 30 minutes of placement. Expected Delivery: {dayjs(order.createdAt).add(30, 'minutes').format("MMM DD, YYYY • h:mm A")} )} {/* Admin Notes */} {order.adminNotes && ( Admin Notes {order.adminNotes} )} {/* Coupon Applied Section */} {order.couponCode && ( Coupon Applied {order.couponCode} {order.couponDescription} Discount Applied: -₹{order.discountAmount} )} {/* Refund Coupon Section */} {order.orderStatus?.refundCouponId && ( Refund Coupon {order.couponCode} Generated refund coupon for order cancellation Value: ₹{order.couponData?.discountAmount} {/* Expires: {order.couponData?. ? dayjs(order.orderStatus.refundCoupon.validTill).format("DD MMM YYYY") : "N/A"} */} )} {/* TEMPORARILY HIDDEN: Refund Details Section */} {/* WARNING: This section contains functional refund and cancellation management features */} {/* DO NOT REMOVE - This is temporarily commented out for UI simplification */} {/* When ready to re-enable, simply uncomment the entire block below */} {/* {order.status === "cancelled" ? "Cancellation Details" : "Refund Details"} {order.status === "cancelled" && order.cancelReason && ( Cancellation Reason {order.cancelReason} )} Refund Status {getRefundStatusText(order.refundStatus)} {order.refundRecord && ( Refund Amount ₹{order.refundRecord.refundAmount} )} {(!Boolean(order.refundRecord)) && {!order.isCod && ( setInitiateRefundDialogOpen(true)} > Initiate Refund )} setGenerateCouponDialogOpen(true)} > Generate Refund Coupon } */} {/* Bottom Action Bar */} {order.status !== "cancelled" && ( setCancelDialogOpen(true)} style={tw`bg-red-500 rounded-xl py-4 items-center shadow-lg mb-3`} > Cancel Order )} router.push("/(drawer)/manage-orders")} style={tw`bg-gray-900 rounded-xl py-4 items-center shadow-lg`} > Manage Orders {/* Generate Coupon Dialog */} setGenerateCouponDialogOpen(false)} > Generate Refund Coupon Create a one-time use coupon for the customer equal to the order amount. Valid for 30 days. This only works for online payment orders. COD orders cannot generate refund coupons. setGenerateCouponDialogOpen(false)} > Cancel {generateCouponMutation.isPending ? "Generating..." : "Generate Coupon"} {/* Initiate Refund Dialog */} setInitiateRefundDialogOpen(false)} > Initiate Refund Process a refund directly to the customer's source account via Razorpay. {/* Refund Type Selection */} Refund Type setRefundType("percent")} > Percentage setRefundType("amount")} > Fixed Amount {/* Refund Value Input */} For COD orders, refunds are processed immediately upon delivery confirmation. setInitiateRefundDialogOpen(false)} > Cancel {initiateRefundMutation.isPending ? "Processing..." : "Confirm Refund"} {/* Cancel Order Dialog */} setCancelDialogOpen(false)} onSuccess={refetch} /> ); }