import { permissions } from "@mrp/shared"; import type { WorkOrderCompletionInput, WorkOrderDetailDto, WorkOrderMaterialIssueInput, WorkOrderStatus } from "@mrp/shared"; import type { WarehouseLocationOptionDto } from "@mrp/shared/dist/inventory/types.js"; import { useEffect, useMemo, useState } from "react"; import { Link, useParams } from "react-router-dom"; import { FileAttachmentsPanel } from "../../components/FileAttachmentsPanel"; import { useAuth } from "../../auth/AuthProvider"; import { api, ApiError } from "../../lib/api"; import { emptyCompletionInput, emptyMaterialIssueInput, workOrderStatusOptions } from "./config"; import { WorkOrderStatusBadge } from "./WorkOrderStatusBadge"; export function WorkOrderDetailPage() { const { token, user } = useAuth(); const { workOrderId } = useParams(); const [workOrder, setWorkOrder] = useState(null); const [locationOptions, setLocationOptions] = useState([]); const [issueForm, setIssueForm] = useState(emptyMaterialIssueInput); const [completionForm, setCompletionForm] = useState(emptyCompletionInput); const [status, setStatus] = useState("Loading work order..."); const [isUpdatingStatus, setIsUpdatingStatus] = useState(false); const [isPostingIssue, setIsPostingIssue] = useState(false); const [isPostingCompletion, setIsPostingCompletion] = useState(false); const canManage = user?.permissions.includes(permissions.manufacturingWrite) ?? false; useEffect(() => { if (!token || !workOrderId) { return; } api.getWorkOrder(token, workOrderId) .then((nextWorkOrder) => { setWorkOrder(nextWorkOrder); setIssueForm({ ...emptyMaterialIssueInput, warehouseId: nextWorkOrder.warehouseId, locationId: nextWorkOrder.locationId, }); setCompletionForm({ ...emptyCompletionInput, quantity: Math.max(nextWorkOrder.dueQuantity, 1), }); setStatus("Work order loaded."); }) .catch((error: unknown) => { const message = error instanceof ApiError ? error.message : "Unable to load work order."; setStatus(message); }); api.getWarehouseLocationOptions(token).then(setLocationOptions).catch(() => setLocationOptions([])); }, [token, workOrderId]); const filteredLocationOptions = useMemo( () => locationOptions.filter((option) => option.warehouseId === issueForm.warehouseId), [issueForm.warehouseId, locationOptions] ); async function handleStatusChange(nextStatus: WorkOrderStatus) { if (!token || !workOrder) { return; } setIsUpdatingStatus(true); setStatus("Updating work-order status..."); try { const nextWorkOrder = await api.updateWorkOrderStatus(token, workOrder.id, nextStatus); setWorkOrder(nextWorkOrder); setStatus("Work-order status updated."); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to update work-order status."; setStatus(message); } finally { setIsUpdatingStatus(false); } } async function handleIssueSubmit(event: React.FormEvent) { event.preventDefault(); if (!token || !workOrder) { return; } setIsPostingIssue(true); setStatus("Posting material issue..."); try { const nextWorkOrder = await api.issueWorkOrderMaterial(token, workOrder.id, issueForm); setWorkOrder(nextWorkOrder); setIssueForm({ ...emptyMaterialIssueInput, warehouseId: nextWorkOrder.warehouseId, locationId: nextWorkOrder.locationId, }); setStatus("Material issue posted."); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to post material issue."; setStatus(message); } finally { setIsPostingIssue(false); } } async function handleCompletionSubmit(event: React.FormEvent) { event.preventDefault(); if (!token || !workOrder) { return; } setIsPostingCompletion(true); setStatus("Posting completion..."); try { const nextWorkOrder = await api.recordWorkOrderCompletion(token, workOrder.id, completionForm); setWorkOrder(nextWorkOrder); setCompletionForm({ ...emptyCompletionInput, quantity: Math.max(nextWorkOrder.dueQuantity, 1), }); setStatus("Completion posted."); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to post completion."; setStatus(message); } finally { setIsPostingCompletion(false); } } if (!workOrder) { return
{status}
; } return (

Work Order

{workOrder.workOrderNumber}

{workOrder.itemSku} - {workOrder.itemName}

Back to work orders {workOrder.projectId ? Open project : null} Open item {canManage ? Edit work order : null}
{canManage ? (

Quick Actions

Release, hold, or close administrative status from the work-order record.

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

Planned

{workOrder.quantity}

Completed

{workOrder.completedQuantity}

Remaining

{workOrder.dueQuantity}

Project

{workOrder.projectNumber || "Unlinked"}

Due Date

{workOrder.dueDate ? new Date(workOrder.dueDate).toLocaleDateString() : "Not set"}

Execution Context

Build item
{workOrder.itemSku} - {workOrder.itemName}
Item type
{workOrder.itemType}
Output location
{workOrder.warehouseCode} / {workOrder.locationCode}
Project customer
{workOrder.projectCustomerName || "Not linked"}

Work Instructions

{workOrder.notes || "No work-order notes recorded."}

{canManage ? (

Material Issue