# 21 · FRONTEND-PHASE2 — Manufacturing: Production Lines (Flows & Rules) > **Purpose:** Frontend source of truth for Phase 2 (Manufacturing): the template canvas builder, the run board, and run execution screens. API contract and all business rules live in `30-BACKEND-PHASE2.md` — this doc never redefines them. Validation posture follows `20-FRONTEND §3`: client validation is UX only; the server is authoritative. Register this doc in `00-CORE.md §7` and `01-DOC-GUIDE.md §2`. --- ## 1. Screens | Screen | Route (suggested) | Actual route (frontend-only build) | Purpose | |---|---|---|---| | Template list | `/production/templates` | `/dashboard/production/templates` — a single shared React Flow canvas, one row per template (header + stages left→right), not a list/grid | Browse templates, status, active-run count; open builder. | | Template builder (canvas) | `/production/templates/{id}` | `/dashboard/production/templates/{id}` | Drag-and-drop stage graph design. | | Run board | `/production/runs` | `/dashboard/production/runs` | All runs with per-stage progress at a glance. | | Start run dialog | modal from board/list | modal from run board | Pick template, target qty, warehouse; preview scaled quantities. | | Run detail | `/production/runs/{id}` | `/dashboard/production/runs/{id}` | Read-only graph with live statuses + stage action drawer. | > **Build status:** §§1–4 are implemented as a **frontend-only mock** (per-page `useState`, no persistence across pages/reloads) — no `Dtos/Production` or `30-BACKEND-PHASE2.md` exist yet, so nothing here talks to a real API. §5 (Run detail) is implemented in a **simplified form**: one generic per-stage advance action instead of the full status-specific stage drawer. §6 (validation/error posture) does not apply yet — there's no server to surface `ProblemDetails`/error codes from. See §8 for the itemized gap list. --- ## 2. Template builder (canvas) **Library:** React Flow (drag/drop nodes, edge drawing, pan/zoom, minimap). Node positions map 1:1 to `posX`/`posY`; the backend stores layout uninterpreted, so all layout behavior is client-owned. **Node (stage card)** shows: name, role label chip, estimated minutes, input count → output count. Selecting a node opens the **stage editor panel**: - Name, role label (free text with suggestions e.g. QA, Assembly), estimated minutes. - **Formula rows** — Inputs: source toggle `Stock | Upstream`; Stock → Item picker (active items only) + UOM + qty/batch; Upstream → dropdown of *direct parents' outputs only* (disable others). Outputs: name + UOM + qty/batch; on the terminal stage the single output requires an Item picker (finished good). - **Custom field builder** — add/remove fields: key (auto-slug from label), label, type (`Text|Number|Checkbox|Date|Select` + options), required toggle. Serialized to the `fieldDefs` jsonb shape verbatim. **Edges:** drawn parent → child. Client blocks duplicate edges and self-loops at draw time. **Client-side graph checks (UX only — server re-validates on save):** - Cycle detection (toposort) — highlight the offending edge. - Exactly one terminal (no-outbound) node — banner "Connect stages so the line converges to a single final stage" when ≠1. - ≥1 entry node; no disconnected nodes (grey them out). - Terminal output has an Item; Upstream inputs reference a current direct parent (re-check after edge deletions and clear broken references with a warning toast). **Save:** full-graph `POST`/`PUT` with `If-Match`. Surface `422 GRAPH_*` codes by focusing the offending node/edge. **Edit lock:** when `activeRunCount > 0`, render the canvas read-only with a banner "Template locked — N run(s) in progress" (server enforces via `409 TEMPLATE_IN_USE`; the banner is UX). Deactivate action instead of delete. --- ## 3. Run board List/grid of runs, newest first, filters: status, template, warehouse, search by doc no. Each row/card: `docNo` (`PRD-2026-00001`), template name, target qty + finished item, created/completed timestamps, rework badge when `reworkCount > 0`, and a **stage progress strip** rendered from `stageSummary` — one segment per stage-status count using the canonical colors: | Status | Color | |---|---| | Waiting | grey `#9CA3AF` | | Ready | blue `#3B82F6` | | InProgress | amber `#F59E0B` | | Done | green `#22C55E` | | Approved | teal `#14B8A6` | | Run Cancelled | red accent on the card | | Run Completed | full teal strip + check | These colors are the single source for status coloring everywhere (board, run graph, drawers, legend). Show a legend on the board. --- ## 4. Start run dialog 1. Template picker (Active only), target quantity (of the finished item, unit shown), warehouse, optional output bin. 2. **Scaled preview:** client computes `scaleFactor = targetQty / terminalOutputQtyPerBatch` and shows every stage's scaled inputs/outputs *as a preview only* — the authoritative scaled figures come back on the `201` response. 3. On create → navigate to run detail. Quantity fine-tuning happens there via the per-stage quantities editor (not in this dialog). - **As built:** stays on the run board with a success toast instead of navigating — the new run's stages start `waiting: stageCount-1, ready: 1` and the user opens it from the board like any other run. --- ## 5. Run detail > **As built (mock):** a single-row React Flow line (header box + one box per real stage name, left→right) instead of the full copied-template graph, with a header progress bar/percentage and one **Give Progress** action (canvas button on the active stage, and a mirrored button in the header) that steps that stage through the canonical status sequence `Waiting → Ready → InProgress → Done → Approved`. No drawer, no per-status action set, no quantities/scrap/custom-field forms, no delivered/available badges, no polling (single local page, no backend to refetch from). All state is local `useState` — reloading the page resets to the seeded mock run. See §8. **Layout:** the template graph re-rendered read-only (same React Flow canvas, positions from the run's copied stages), each node colored by live status, with `deliveredQty/plannedQty` badges on inbound edges and an available-to-transfer badge on approved stages holding a remainder. Poll or refetch after every action. **Stage drawer** (click a node) — content by status: - Any status: name, role chip, estimated vs **actual** time (`actualStartAt`/`actualEndAt`, live elapsed while InProgress), event history timeline. - **Waiting:** per-upstream-input delivery progress bars; nothing actionable except *Reject intake* when `deliveredQty > 0` (see below). - **Ready:** *Edit quantities* (planned in/out — disabled after start, surface `409 STAGE_NOT_EDITABLE`), Stock-input availability hints (`on-hand` enquiry, advisory only — never block client-side, per `20-FRONTEND §3`), and **Start**. On start errors surface `STOCK_NEGATIVE_BLOCKED` / `ONHOLD_NOT_ISSUABLE` / `EXPIRED_BATCH_BLOCKED` with the item named. - **InProgress:** **Complete** form — per output: produced qty, scrapped qty (reason-code picker appears and becomes required when scrap > 0), plus the **custom field form rendered from `fieldDefs`** (required fields block submit client-side; server backs with `400 REQUIRED_FIELD_MISSING`). - **Done:** **Approve** — non-terminal: default "transfer all" with an optional per-output partial amount (validated ≤ available); terminal: confirmation summarizing the receipt (qty, computed unit cost from cost pool preview). Terminal also offers **Reject** with a strong confirm modal: *"This resets the entire run to its starting stages (rework #N). Consumed materials remain in the run."* - **Approved (non-terminal):** *Transfer remainder* action while available > 0 (`422 TRANSFER_EXCEEDS_AVAILABLE` surfaced inline). - **Reject intake** (on a Ready/Waiting stage with deliveries): confirm modal *"Returns work to the previous completed stage for rework"* → parents visibly flip back to InProgress on refresh. **Run-level actions:** *Return leftover* (per started Stock input: qty ≤ consumed − returned, reason code required; hidden once run Completed — `RUN_COST_CLOSED`), *Cancel run* (reason code + note, confirm modal explaining stock return; hidden when Completed). --- ## 6. Validation posture & error surfacing - Client checks: required/format/range, graph checks (§2), qty ≤ available style guards — all UX; never assume stock rules client-side. - Every `ProblemDetails` renders its `title`; map domain `code`s to friendly inline messages (table in `30-BACKEND-PHASE2 §D.4`). Unknown codes fall back to the ProblemDetails title + trace id. - `412 CONCURRENCY_CONFLICT` → "This item changed elsewhere — reloading" + refetch. Stage-action `409`s (wrong status) → refetch the run silently and re-render; another user likely acted first. - Stage-transition posts send an `Idempotency-Key` (uuid per click) so double-clicks are replay-safe. --- ## 7. Foundation additions (PROGRESS seed) - [x] React Flow dependency + canvas components — but **not** a single shared editable/read-only variant: the template-overview canvas (`templates/page.tsx`), the builder canvas (`templates/[id]/page.tsx`), and the run-detail canvas (`runs/[id]/page.tsx`) are three separate node-type sets (`ProductionLineNodes.tsx`, `StageNode.tsx`/`AnnotationNodes.tsx`, `RunStageNode.tsx`). - [ ] Types mirroring `Dtos/Production` (template graph, run graph, stage actions) — not started; `types/production.ts` is a standalone frontend-only placeholder shape, nothing to mirror against yet. - [x] Status-color tokens (§3 table) exported from one module — `lib/production-status-colors.ts` (`STAGE_STATUS_COLOR`/`_LABEL`/`_ORDER`, `RUN_CANCELLED_COLOR`, `RUN_COMPLETED_COLOR`). - [~] Custom-field renderer (defs jsonb → form) + builder (form → defs jsonb) — builder half only (`StageEditorPanel.tsx`, defs jsonb ← form). The runtime renderer (form → filled values, used during the spec'd Complete action) doesn't exist since there's no stage drawer/Complete step (§5). - [~] Screens: template list · builder · run board · start dialog · run detail + drawer — list/builder/board/dialog implemented (as mock); run detail implemented **without** the drawer or per-status action set (§5, §8). - [ ] Error-code → message map for §D.4 additions — not started, no backend/`ProblemDetails` to map yet. --- ## 8. Gaps vs. this spec (frontend-only mock — no `Dtos/Production` / `30-BACKEND-PHASE2.md` yet) Everything below is intentional scope for the current build, not a bug — recorded so whoever wires up the real backend knows exactly what's still owed against this doc: - **No persistence.** All state is per-page `useState` seeded from hardcoded mock arrays (`lib/production-mock-templates.ts`, `lib/production-mock-runs.ts`). Templates, runs, and stage-status edits don't survive a reload and don't sync across the three canvases/pages. - **Run detail is a simplified single action, not the stage drawer (§5).** One generic "Give Progress" step (Waiting→Ready→InProgress→Done→Approved) replaces Start/Complete (qty+scrap+custom fields)/Approve/Reject/Transfer remainder/Reject intake. No event history timeline, no actual-vs-estimated time tracking, no delivered/available badges. - **No run-level actions.** Return leftover and Cancel run (§5) aren't implemented. - **Stage identity on the run board/detail is reconstructed, not authoritative.** `ProductionRun.stageSummary` only carries counts per status; `buildStagePlan()` (`lib/production-mock-runs.ts`) maps those counts onto the template's real stage names most-complete-first as a display approximation — a real backend would return named per-stage records directly. - **No validation/error posture (§6).** No `ProblemDetails`, no domain error-code mapping, no `412`/`409` handling, no `Idempotency-Key` — there's no server to produce any of it yet. - **Save uses no `If-Match`/concurrency token** on the builder (§2) — a local `locked` boolean (from mock `activeRunCount`) stands in for the server's `409 TEMPLATE_IN_USE` edit lock. - **Template overview deviates from "list" (§1).** Implemented as one shared canvas (all templates as production lines, one row each) instead of a browsable list/grid, per explicit product direction during the build. *End of 21-FRONTEND-PHASE2.md. Contract: `30-BACKEND-PHASE2.md`. Record work: `Frontend/PROGRESS.md`.*