// Shop-floor dashboard. // // Polls /api/v1/production/work-orders/shop-floor every 5s and // renders one card per IN_PROGRESS work order with its current // operation, planned vs actual minutes, and operations completed. // Designed to be projected on a wall-mounted screen — the cards // are large, the typography is high-contrast, and the only state // is "what's running right now". import { useEffect, useState } from 'react' import { Link } from 'react-router-dom' import { production } from '@/api/client' import type { ShopFloorEntry } from '@/types/api' import { PageHeader } from '@/components/PageHeader' import { Loading } from '@/components/Loading' import { ErrorBox } from '@/components/ErrorBox' import { StatusBadge } from '@/components/StatusBadge' const POLL_MS = 5000 export function ShopFloorPage() { const [rows, setRows] = useState([]) const [error, setError] = useState(null) const [loading, setLoading] = useState(true) const [updatedAt, setUpdatedAt] = useState(null) useEffect(() => { let active = true let timer: number | null = null const tick = async () => { try { const data = await production.shopFloor() if (!active) return setRows(data) setUpdatedAt(new Date()) setError(null) } catch (e: unknown) { if (active) setError(e instanceof Error ? e : new Error(String(e))) } finally { if (active) setLoading(false) } if (active) timer = window.setTimeout(tick, POLL_MS) } tick() return () => { active = false if (timer !== null) window.clearTimeout(timer) } }, []) return (
{loading && } {error && } {!loading && rows.length === 0 && (
No work orders are in progress right now.
)}
{rows.map((r) => { const std = Number(r.totalStandardMinutes) const act = Number(r.totalActualMinutes) const pct = std > 0 ? Math.min(100, Math.round((act / std) * 100)) : 0 return (
{r.workOrderCode} {r.operationsCompleted} / {r.operationsTotal} ops
Output: {r.outputItemCode} ×{' '} {String(r.outputQuantity)}
Current operation
{r.currentOperationCode ? (
{r.currentOperationCode} @ {r.currentWorkCenter} {r.currentOperationStatus && }
) : (
No routing
)}
{act.toFixed(0)} actual min {std.toFixed(0)} std min
) })}
) }