diff --git a/frontend/package/components/ShipmentTimeline/ShipmentTimeline.tsx b/frontend/package/components/ShipmentTimeline/ShipmentTimeline.tsx new file mode 100644 index 00000000..afd2ed01 --- /dev/null +++ b/frontend/package/components/ShipmentTimeline/ShipmentTimeline.tsx @@ -0,0 +1,188 @@ +'use client'; + +import { cn } from '@/lib/utils'; +import type { ShipmentStatusHistory, ShipmentStatus } from '@/types/shipment.types'; + +interface TimelineStep { + status: ShipmentStatus; + label: string; + timestamp?: string | null; + actor?: string; + reason?: string | null; +} + +const STEPS: { status: ShipmentStatus; label: string }[] = [ + { status: 'pending' as ShipmentStatus, label: 'Created' }, + { status: 'accepted' as ShipmentStatus, label: 'Accepted' }, + { status: 'in_transit' as ShipmentStatus, label: 'In Transit' }, + { status: 'delivered' as ShipmentStatus, label: 'Delivered' }, + { status: 'completed' as ShipmentStatus, label: 'Completed' }, +]; + +const TERMINAL_STATUSES: ShipmentStatus[] = [ + 'cancelled' as ShipmentStatus, + 'disputed' as ShipmentStatus, +]; + +export interface ShipmentTimelineProps { + history: ShipmentStatusHistory[]; + currentStatus?: ShipmentStatus | null; +} + +export function ShipmentTimeline({ history, currentStatus }: ShipmentTimelineProps) { + const isTerminal = currentStatus && TERMINAL_STATUSES.includes(currentStatus); + + // Build steps from history + const completedStatuses = new Set(history.map((h) => h.toStatus)); + const lastCompleted = history.length > 0 ? history[history.length - 1] : null; + + // If terminal, show a special indicator + if (isTerminal && currentStatus) { + const terminalLabel = + currentStatus === 'cancelled' ? 'Cancelled' : 'Disputed'; + const terminalColor = + currentStatus === 'cancelled' + ? 'bg-amber-500' + : 'bg-red-500'; + + return ( +
+ {entry.toStatus.replace('_', ' ').replace(/\b\w/g, (l) => l.toUpperCase())} +
+ + {entry.reason && ( ++ “{entry.reason}” +
+ )} ++ {terminalLabel} +
+ {lastCompleted?.changedAt && ( + + )} ++ {step.label} + {isCurrent && ( + + Active + + )} +
+ {historyEntry && ( + <> + + {historyEntry.changedBy && ( ++ by {historyEntry.changedBy.firstName} {historyEntry.changedBy.lastName} +
+ )} + > + )} + {historyEntry?.reason && ( ++ “{historyEntry.reason}” +
+ )} +