// Work-order detail screen — read-only header + start/complete // action verbs that drive the v2 state machine. The shop-floor // dashboard at /shop-floor handles the per-operation walk for v3 // routing-equipped orders. v1 SPA keeps this screen simple: start a // DRAFT, complete an IN_PROGRESS into a warehouse. import { useCallback, useEffect, useState } from 'react' import { useNavigate, useParams } from 'react-router-dom' import { inventory, production } from '@/api/client' import type { Location, WorkOrder } from '@/types/api' import { PageHeader } from '@/components/PageHeader' import { Loading } from '@/components/Loading' import { ErrorBox } from '@/components/ErrorBox' import { StatusBadge } from '@/components/StatusBadge' export function WorkOrderDetailPage() { const { id = '' } = useParams<{ id: string }>() const navigate = useNavigate() const [order, setOrder] = useState(null) const [locations, setLocations] = useState([]) const [outputLocation, setOutputLocation] = useState('') const [loading, setLoading] = useState(true) const [acting, setActing] = useState(false) const [error, setError] = useState(null) const [actionMessage, setActionMessage] = useState(null) const refresh = useCallback(async () => { const o = await production.getWorkOrder(id) setOrder(o) }, [id]) useEffect(() => { let active = true setLoading(true) Promise.all([production.getWorkOrder(id), inventory.listLocations()]) .then(([o, locs]: [WorkOrder, Location[]]) => { if (!active) return setOrder(o) setLocations(locs.filter((l) => l.active)) const firstWarehouse = locs.find((l) => l.active && l.type === 'WAREHOUSE') setOutputLocation(firstWarehouse?.code ?? locs[0]?.code ?? '') }) .catch((e: unknown) => { if (active) setError(e instanceof Error ? e : new Error(String(e))) }) .finally(() => active && setLoading(false)) return () => { active = false } }, [id]) if (loading) return if (error) return if (!order) return const onStart = async () => { setActing(true) setError(null) setActionMessage(null) try { await production.startWorkOrder(order.id) await refresh() setActionMessage('Started. Operations can now be walked from the Shop Floor screen.') } catch (e: unknown) { setError(e instanceof Error ? e : new Error(String(e))) } finally { setActing(false) } } const onComplete = async () => { if (!outputLocation) { setError(new Error('Pick an output location first.')) return } setActing(true) setError(null) setActionMessage(null) try { await production.completeWorkOrder(order.id, outputLocation) await refresh() setActionMessage( `Completed. Materials issued, finished goods credited to ${outputLocation}.`, ) } catch (e: unknown) { setError(e instanceof Error ? e : new Error(String(e))) } finally { setActing(false) } } const canStart = order.status === 'DRAFT' const canComplete = order.status === 'IN_PROGRESS' return (
navigate('/work-orders')}> ← Back } />
Status:
{actionMessage && (
{actionMessage}
)}

Actions

BOM inputs

{order.inputs.length === 0 ? (
No BOM lines.
) : ( {order.inputs.map((i) => ( ))}
# Item Qty / unit Source loc
{i.lineNo} {i.itemCode} {String(i.quantityPerUnit)} {i.sourceLocationCode}
)}

Routing operations

{order.operations.length === 0 ? (
No routing.
) : ( {order.operations.map((o) => ( ))}
# Operation Work center Std min Status
{o.lineNo} {o.operationCode} {o.workCenter} {String(o.standardMinutes)}
)}
) }