import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet, ScrollView, TouchableOpacity, ActivityIndicator, Alert, } from 'react-native'; import { useRouter, useLocalSearchParams } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import api from '../services/api'; interface OrderDetail { orderId: number; userId: number; userName: string; totalPrice: number; orderStatus: string; shippingAddress: string; createdAt: string; updatedAt: string; orderItems: { orderItemId: number; productId: number; productName: string; quantity: number; price: number; subtotal: number; }[]; } interface Timeline { status: string; timestamp: string | null; completed: boolean; } const STATUS_COLORS: Record = { PENDING: '#FFA726', CONFIRMED: '#42A5F5', SHIPPING: '#AB47BC', DELIVERED: '#66BB6A', CANCELLED: '#EF5350', }; const STATUS_LABELS: Record = { PENDING: 'Chờ xác nhận', CONFIRMED: 'Đã xác nhận', SHIPPING: 'Đang giao hàng', DELIVERED: 'Đã giao hàng', CANCELLED: 'Đã hủy', }; export default function OrderDetailScreen() { const router = useRouter(); const params = useLocalSearchParams(); const orderId = params.id; const [order, setOrder] = useState(null); const [timeline, setTimeline] = useState([]); const [loading, setLoading] = useState(true); const [cancelling, setCancelling] = useState(false); useEffect(() => { if (orderId) { loadOrderDetail(); loadOrderProgress(); } }, [orderId]); /** 🔹 Load chi tiết đơn hàng */ const loadOrderDetail = async () => { try { const res = await api.get(`/orders/${orderId}`, { requireAuth: true, }); // Fix: Safely check 'res' type before accessing properties if (res && typeof res === 'object' && 'success' in res && (res as any).success) { setOrder((res as any).data); } } catch (error) { console.error('Load order detail error:', error); Alert.alert('Lỗi', 'Không thể tải chi tiết đơn hàng.'); } finally { setLoading(false); } }; /** 🔹 Load tiến trình đơn hàng */ const loadOrderProgress = async () => { try { const res = await api.get(`/orders/${orderId}/progress`, { requireAuth: true, }); // Fix: safely check 'res' type before accessing properties if (res && typeof res === 'object' && 'success' in res && (res as any).success) { setTimeline((res as any).data.timeline); } } catch (error) { console.error('Load order progress error:', error); } }; /** 🔹 Hủy đơn hàng */ const handleCancelOrder = () => { Alert.alert( 'Hủy đơn hàng', 'Bạn có chắc chắn muốn hủy đơn hàng này?', [ { text: 'Không', style: 'cancel' }, { text: 'Có', onPress: cancelOrder, style: 'destructive' }, ] ); }; const cancelOrder = async () => { setCancelling(true); try { const res = await api.put(`/orders/${orderId}/cancel`, undefined, { requireAuth: true, }); // Fix: 'res' is of type 'unknown', so first safely cast/type guard if (res && typeof res === 'object' && 'success' in res && (res as any).success) { Alert.alert('Thành công', 'Đơn hàng đã được hủy'); loadOrderDetail(); loadOrderProgress(); } else { let message = 'Không thể hủy đơn hàng'; if (res && typeof res === 'object' && 'message' in res) { // @ts-ignore message = res.message || message; } Alert.alert('Lỗi', message); } } catch (error) { Alert.alert('Lỗi', 'Không thể hủy đơn hàng'); } finally { setCancelling(false); } }; const formatDate = (dateString: string) => { const date = new Date(dateString); return date.toLocaleDateString('vi-VN', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }); }; if (loading) { return ( ); } if (!order) { return ( Không tìm thấy đơn hàng ); } return ( {/* Header */} router.back()}> Chi tiết đơn hàng {/* Order status */} {STATUS_LABELS[order.orderStatus]} Đơn hàng #{order.orderId} Đặt hàng: {formatDate(order.createdAt)} {/* Timeline */} {order.orderStatus !== 'CANCELLED' && ( Tiến độ đơn hàng {timeline.map((step, idx) => ( {step.completed && ( )} {idx < timeline.length - 1 && ( )} {STATUS_LABELS[step.status]} {step.timestamp && ( {formatDate(step.timestamp)} )} ))} )} {/* Shipping address */} Địa chỉ giao hàng {order.shippingAddress} {/* Items */} Sản phẩm {order.orderItems.map((item) => ( {item.productName} x{item.quantity} {item.price.toLocaleString('vi-VN')}₫ {item.subtotal.toLocaleString('vi-VN')}₫ ))} {/* Summary */} Thanh toán Tạm tính {order.totalPrice.toLocaleString('vi-VN')}₫ Phí vận chuyển 0₫ Tổng cộng {order.totalPrice.toLocaleString('vi-VN')}₫ {/* Cancel Order */} {(order.orderStatus === 'PENDING' || order.orderStatus === 'CONFIRMED') && ( {cancelling ? ( ) : ( Hủy đơn hàng )} )} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#F5F5F5', }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', padding: 16, backgroundColor: '#fff', borderBottomWidth: 1, borderBottomColor: '#E0E0E0', }, headerTitle: { fontSize: 18, fontWeight: 'bold', }, statusSection: { backgroundColor: '#fff', padding: 20, alignItems: 'center', marginTop: 10, }, statusBadge: { paddingHorizontal: 20, paddingVertical: 8, borderRadius: 20, marginBottom: 12, }, statusText: { color: '#fff', fontSize: 14, fontWeight: 'bold', }, orderId: { fontSize: 18, fontWeight: 'bold', marginBottom: 4, }, orderDate: { fontSize: 14, color: '#666', }, section: { backgroundColor: '#fff', marginTop: 10, padding: 16, }, sectionTitle: { fontSize: 16, fontWeight: 'bold', marginBottom: 12, }, timelineItem: { flexDirection: 'row', marginBottom: 8, }, timelineLeft: { alignItems: 'center', marginRight: 12, }, timelineDot: { width: 24, height: 24, borderRadius: 12, backgroundColor: '#E0E0E0', alignItems: 'center', justifyContent: 'center', }, timelineDotActive: { backgroundColor: '#4CAF50', }, timelineLine: { width: 2, flex: 1, backgroundColor: '#E0E0E0', marginTop: 4, }, timelineLineActive: { backgroundColor: '#4CAF50', }, timelineRight: { flex: 1, paddingBottom: 16, }, timelineStatus: { fontSize: 14, color: '#999', marginBottom: 4, }, timelineStatusActive: { color: '#333', fontWeight: 'bold', }, timelineDate: { fontSize: 12, color: '#999', }, addressText: { fontSize: 14, color: '#333', lineHeight: 20, }, productItem: { flexDirection: 'row', justifyContent: 'space-between', paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: '#F0F0F0', }, productInfo: { flex: 1, marginRight: 12, }, productName: { fontSize: 14, marginBottom: 4, }, productQuantity: { fontSize: 12, color: '#666', }, productPrices: { alignItems: 'flex-end', }, productPrice: { fontSize: 12, color: '#666', marginBottom: 4, }, productSubtotal: { fontSize: 14, fontWeight: 'bold', color: '#FF6B6B', }, summaryRow: { flexDirection: 'row', justifyContent: 'space-between', paddingVertical: 8, }, summaryLabel: { fontSize: 14, color: '#666', }, summaryValue: { fontSize: 14, }, totalRow: { borderTopWidth: 1, borderTopColor: '#E0E0E0', marginTop: 8, paddingTop: 12, }, totalLabel: { fontSize: 16, fontWeight: 'bold', }, totalValue: { fontSize: 18, fontWeight: 'bold', color: '#FF6B6B', }, cancelButton: { backgroundColor: '#EF5350', margin: 16, padding: 16, borderRadius: 8, alignItems: 'center', }, buttonDisabled: { opacity: 0.6, }, cancelButtonText: { color: '#fff', fontSize: 16, fontWeight: 'bold', }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, errorContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, });