import { permissions } from "@mrp/shared"; import type { ShipmentDetailDto, ShipmentStatus, ShipmentSummaryDto } from "@mrp/shared/dist/shipping/types.js"; import { useEffect, useState } from "react"; import { Link, useParams } from "react-router-dom"; import { useAuth } from "../../auth/AuthProvider"; import { api, ApiError } from "../../lib/api"; import { shipmentStatusOptions } from "./config"; import { ShipmentStatusBadge } from "./ShipmentStatusBadge"; export function ShipmentDetailPage() { const { token, user } = useAuth(); const { shipmentId } = useParams(); const [shipment, setShipment] = useState(null); const [relatedShipments, setRelatedShipments] = useState([]); const [status, setStatus] = useState("Loading shipment..."); const [isUpdatingStatus, setIsUpdatingStatus] = useState(false); const [isRenderingPdf, setIsRenderingPdf] = useState(false); const canManage = user?.permissions.includes(permissions.shippingWrite) ?? false; useEffect(() => { if (!token || !shipmentId) { return; } api.getShipment(token, shipmentId) .then((nextShipment) => { setShipment(nextShipment); setStatus("Shipment loaded."); return api.getShipments(token, { salesOrderId: nextShipment.salesOrderId }); }) .then((shipments) => setRelatedShipments(shipments.filter((candidate) => candidate.id !== shipmentId))) .catch((error: unknown) => { const message = error instanceof ApiError ? error.message : "Unable to load shipment."; setStatus(message); }); }, [shipmentId, token]); async function handleStatusChange(nextStatus: ShipmentStatus) { if (!token || !shipment) { return; } setIsUpdatingStatus(true); setStatus("Updating shipment status..."); try { const nextShipment = await api.updateShipmentStatus(token, shipment.id, nextStatus); setShipment(nextShipment); setStatus("Shipment status updated."); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to update shipment status."; setStatus(message); } finally { setIsUpdatingStatus(false); } } async function handleOpenPackingSlip() { if (!token || !shipment) { return; } setIsRenderingPdf(true); setStatus("Rendering packing slip PDF..."); try { const blob = await api.getShipmentPackingSlipPdf(token, shipment.id); const objectUrl = URL.createObjectURL(blob); window.open(objectUrl, "_blank", "noopener,noreferrer"); window.setTimeout(() => URL.revokeObjectURL(objectUrl), 60_000); setStatus("Packing slip PDF rendered."); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to render packing slip PDF."; setStatus(message); } finally { setIsRenderingPdf(false); } } if (!shipment) { return
{status}
; } return (

Shipment

{shipment.shipmentNumber}

{shipment.salesOrderNumber} · {shipment.customerName}

Back to shipments Open sales order {canManage ? ( Edit shipment ) : null}
{canManage ? (

Quick Actions

Update shipment status without opening the editor.

{shipmentStatusOptions.map((option) => ( ))}
) : null}

Carrier

{shipment.carrier || "Not set"}

Service

{shipment.serviceLevel || "Not set"}

Tracking

{shipment.trackingNumber || "Not set"}

Packages

{shipment.packageCount}

Shipment Notes

{shipment.notes || "No notes recorded for this shipment."}

Timing

Ship Date
{shipment.shipDate ? new Date(shipment.shipDate).toLocaleDateString() : "Not set"}
Created
{new Date(shipment.createdAt).toLocaleString()}
Updated
{new Date(shipment.updatedAt).toLocaleString()}

Related Shipments

Other shipments already tied to this sales order.

{canManage ? ( Add another shipment ) : null}
{relatedShipments.length === 0 ? (
No additional shipments exist for this sales order.
) : (
{relatedShipments.map((related) => (
{related.shipmentNumber}
{related.carrier || "Carrier not set"} · {related.trackingNumber || "No tracking"}
))}
)}
); }