import { useParams, useNavigate } from 'react-router-dom'; import { Avatar, Box, Button, Chip, Grid, Paper, Stack, Step, StepLabel, Stepper, Typography, useMediaQuery } from '@mui/material'; import { useTheme } from '@mui/material/styles'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { MdArrowBack, MdLocalShipping, MdPerson, MdPhone, MdLocationOn, MdTwoWheeler, MdStar, MdOutlineSmartToy, MdOutlineAssignmentInd, MdOutlineCancel } from 'react-icons/md'; import Loader from 'components/Loader'; import { OpenToast } from 'components/third-party/OpenToast'; import { getorderdetails, autoAssignBooking, cancelOrder } from 'pages/api/api'; const DT = { radiusCard: 16, textPrimary: '#0f172a', textSecondary: '#64748b', textMuted: '#94a3b8', borderSubtle: '#e2e8f0', divider: '#f1f5f9', surface: '#ffffff', surfaceAlt: '#f8fafc' }; const a = (c, suffix) => `${c}${suffix}`; const tint = (c) => a(c, '08'); const soft = (c) => a(c, '18'); const edge = (c) => a(c, '55'); const BRAND = '#C01227'; // Ordered status progression used for the stepper. Doormile's real status // vocabulary — Assignment_Failed and Cancelled are terminal/off-path states // shown separately rather than as stepper nodes. const STATUS_STEPS = ['Pending_Pickup', 'Miler_Assigned', 'Pickup_Scheduled', 'At_Customer', 'Picked_Up', 'At_Hub', 'Delivered']; const STEP_LABELS = { Pending_Pickup: 'Pending Pickup', Miler_Assigned: 'Miler Assigned', Pickup_Scheduled: 'Pickup Scheduled', At_Customer: 'At Customer', Picked_Up: 'Picked Up', At_Hub: 'At Hub', Delivered: 'Delivered' }; const InfoRow = ({ icon: Icon, label, value, color = BRAND }) => ( {label} {value || '—'} ); const BookingDetail = () => { const { id } = useParams(); const navigate = useNavigate(); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('md')); const queryClient = useQueryClient(); const { data, isLoading, isError } = useQuery({ queryKey: ['bookingDetail', id], queryFn: () => getorderdetails(id), enabled: Boolean(id) }); // Confirmed shape (booking id 5): bookingid, bookingreference, status, // pickupaddress, deliveryaddress, assignedmileruserid, createdat, // bookingparcels (nested parcels), serviceoptions, payments. const booking = data?.data || data || {}; const reassignMutation = useMutation({ mutationFn: () => autoAssignBooking(id), onSuccess: () => { OpenToast('Reassignment triggered', 'success', 2000); queryClient.invalidateQueries({ queryKey: ['bookingDetail', id] }); }, onError: (err) => OpenToast(err.response?.data?.message || err.message, 'error', 3000) }); const cancelMutation = useMutation({ mutationFn: () => cancelOrder(id), onSuccess: () => { OpenToast('Booking cancelled', 'success', 2000); queryClient.invalidateQueries({ queryKey: ['bookingDetail', id] }); }, onError: (err) => OpenToast(err.response?.data?.message || err.message, 'error', 3000) }); if (isLoading) return ; const status = booking.status || 'Pending_Pickup'; const isTerminal = ['cancelled', 'assignment_failed'].includes(String(status).toLowerCase()); const activeStepIdx = STATUS_STEPS.indexOf(status); const parcels = booking.bookingparcels || booking.parcels || []; const miler = booking.miler || booking.assignedmiler || null; const agentDecision = booking.agent_decision_id ? booking.agentdecision || booking.agent_decision : null; return ( <> {booking.bookingreference || `Booking #${id}`} {booking.createdat ? new Date(booking.createdat).toLocaleString() : '—'} {isError && ( Could not load full booking details — showing whatever came back. )} {/* Status timeline */} {!isTerminal && ( {STATUS_STEPS.map((s) => ( {STEP_LABELS[s]} ))} )} Customer & Pickup Delivery Assigned Miler {booking.assignedmileruserid || miler ? ( {miler?.displayname || `Miler #${booking.assignedmileruserid}`} {miler?.phone && ( {miler.phone} )} {miler?.rating != null && ( {miler.rating} )} ) : ( No miler assigned yet. )} Parcel Details {parcels.length === 0 ? ( No parcel details available. ) : ( {parcels.map((p, i) => ( {p.description || p.name || `Parcel ${i + 1}`} {p.weight ? `${p.weight}kg` : ''} ))} )} {agentDecision && ( AI Assignment Reasoning {agentDecision.reasoning || agentDecision.reason || JSON.stringify(agentDecision)} )} {!isTerminal && ( )} {!isTerminal && ( )} ); }; export default BookingDetail;