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 { FileAttachmentsPanel } from "../../components/FileAttachmentsPanel"; 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 [activeDocumentAction, setActiveDocumentAction] = useState<"packing-slip" | "label" | "bol" | null>(null); 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 handleOpenDocument(kind: "packing-slip" | "label" | "bol") { if (!token || !shipment) { return; } setActiveDocumentAction(kind); setStatus( kind === "packing-slip" ? "Rendering packing slip PDF..." : kind === "label" ? "Rendering shipping label PDF..." : "Rendering bill of lading PDF..." ); try { const blob = kind === "packing-slip" ? await api.getShipmentPackingSlipPdf(token, shipment.id) : kind === "label" ? await api.getShipmentLabelPdf(token, shipment.id) : await api.getShipmentBillOfLadingPdf(token, shipment.id); const objectUrl = URL.createObjectURL(blob); window.open(objectUrl, "_blank", "noopener,noreferrer"); window.setTimeout(() => URL.revokeObjectURL(objectUrl), 60_000); setStatus( kind === "packing-slip" ? "Packing slip PDF rendered." : kind === "label" ? "Shipping label PDF rendered." : "Bill of lading PDF rendered." ); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : kind === "packing-slip" ? "Unable to render packing slip PDF." : kind === "label" ? "Unable to render shipping label PDF." : "Unable to render bill of lading PDF."; setStatus(message); } finally { setActiveDocumentAction(null); } } 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"}
))}
)}
); }