Files
ERP-core/docs/21-FRONTEND-PHASE2.md
T
ImanThiyanga 7d6e597389 feat: add production templates API and documentation for manufacturing phase 2
- Implemented CRUD operations for production templates, including listing, retrieving, creating, updating, and deactivating templates.
- Introduced a new API contract for production runs, detailing the lifecycle from creation to completion, including handling of stock inputs and outputs.
- Documented the architecture, requirements, entity model, and API contract for the manufacturing phase 2, ensuring clarity on the production process and its integration with existing systems.
2026-07-31 10:22:40 +05:30

143 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 (as built) | 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 (updated 2026-07-30).** The **backend is complete** — `Dtos/Production`, both controllers and the full `30-BACKEND-PHASE2.md` contract all exist, last verified at 312/312 smoke assertions (`Backend/smoke/run_all.py`). **Every frontend screen is now wired to it and both mock modules are deleted:**
>
> | Piece | State |
> |---|---|
> | Contract layer — `types/production.ts`, `lib/api/production-templates.ts`, `lib/api/production-runs.ts`, error-code map | **Done**, mirrors §D.1D.4 |
> | §1 Template overview | **Real API** — live list, debounced search, status filter, paging |
> | §2 Template builder | **Real API** — loads/saves the graph with server keys, `If-Match`, persisted positions, real item/UOM pickers |
> | §3 Run board · §4 Start dialog | **Real API** — filters + paging; the dialog posts and navigates to the run |
> | §5 Run detail + stage drawer | **Real API** — canvas from the run's own positions/edges, and the full per-status action set |
> | §6 Validation posture | **Done** — `412` banner, server-driven `409 TEMPLATE_IN_USE` lock, per-action `Idempotency-Key`, silent stage-status `409` refetch |
>
> **Caveat, stated plainly: none of it has been driven in a browser.** The tree type-checks and Turbopack compiles it, but no screen has been clicked and no end-to-end run walked through the UI — blocked on AuthHex being unable to issue a token. §8 records the remaining live deviations; `Frontend/PROGRESS.md` §§1113 is the authoritative state.
---
## 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 — this section is implemented as specified, with three deviations worth naming:**
>
> - **Refetch, not poll.** The page reloads the whole run after every action rather than polling on a timer; the drawer's body is switched on the *server's* stage status, so nothing is ever rendered from a local guess. A stage-status `409` also triggers a silent refetch (§6), which covers the "someone else acted first" case a poll would have caught.
> - **No inbound-edge badges; intake lives on the node.** React Flow edge labels are cramped and a stage can have several upstream inputs from the same parent, so the aggregate `delivered/planned` badge sits on the *stage card* — colour-coded, so it doubles as an explanation of why a stage is still `Waiting`. The available-to-transfer badge is on the card too.
> - **Delivery progress is a labelled figure, not a bar.** Per-upstream-input `delivered / planned` reads better as numbers in the drawer's input list than as a row of bars, and it keeps the base-UOM vs declared-UOM distinction visible (see the consumed/returned note in `30-BACKEND-PHASE2 §D.3`).
>
> Files: `runs/[id]/page.tsx` (canvas + header), `StageDrawer.tsx` (shell + every per-status body + event timeline), `CustomFieldForm.tsx` (runtime field renderer), `RunActions.tsx` (run-level Return leftover / Cancel).
**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`).
- [x] Types mirroring `Dtos/Production` (template graph, run graph, stage actions) — `types/production.ts`, rewritten against `30-BACKEND-PHASE2 §D`. The placeholder shapes are gone.
- [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`).
- [x] Custom-field renderer (defs jsonb → form) + builder (form → defs jsonb) — both halves: the designer in `StageEditorPanel.tsx`, and the runtime renderer in `runs/[id]/CustomFieldForm.tsx`, used by the drawer's Complete action.
- [x] Screens: template list · builder · run board · start dialog · run detail + drawer — all implemented against the real API.
- [x] Error-code → message map for §D.4 additions — all 17 codes in `lib/error-map.ts`.
---
## 8. Gaps vs. this spec
> **Closed.** Everything below was intentional scope while no backend existed; the backend is built and verified and every screen is now wired to it. Struck-through items are done; what remains is a short list of live deviations and one honest caveat.
- ~~**No persistence.**~~ Both mock modules (`lib/production-mock-templates.ts`, `lib/production-mock-runs.ts`) are **deleted**. Every screen reads and writes the real API.
- ~~**Run detail is a simplified single action, not the stage drawer (§5).**~~ The drawer (`runs/[id]/StageDrawer.tsx`) implements the full per-status action set — Start, Complete (qty + scrap + reason + custom fields), Approve with optional partial transfer, the terminal receipt confirmation, Reject for rework, Transfer remainder, Reject intake — plus the per-stage event timeline and estimated-vs-actual time with a live elapsed counter while a stage is running.
- ~~**No run-level actions.**~~ `runs/[id]/RunActions.tsx` implements Return leftover and Cancel run, both hidden once the run leaves `InProgress`.
- ~~**Stage identity on the run board/detail is reconstructed, not authoritative.**~~ `buildStagePlan()` is deleted. Run detail renders named per-stage records straight from `GET /production-runs/{id}`. **The board deliberately does not name stages at all** — its list projection carries `stageSummary` counts only, so it shows counts per status rather than guessing which stage holds which count.
- ~~**Validation/error posture (§6).**~~ Fully closed: the `412` conflict banner, the server-driven `409 TEMPLATE_IN_USE` lock banner (with a distinct message for the mid-edit TOCTOU case), per-action `Idempotency-Key`, and silent refetch on stage-status `409`s via `isStaleStageError()`.
- ~~**Save uses no `If-Match`/concurrency token** on the builder (§2).~~ It holds the ETag from the GET and sends `If-Match` on every `PUT`; the lock now comes from the server's `activeRunCount`, not a local boolean.
- **Template overview deviates from "list" (§1).** Still one shared canvas (all templates as production lines, one row each) instead of a browsable list/grid, per explicit product direction during the build. It now has a real debounced search, status filter and pagination around it.
- **`422 GRAPH_*` node focus is a substring match, not a structured reference.** The server's `detail` names the offending stages rather than returning their keys, so the builder highlights any stage whose name appears in the message. Advisory by design: the full message is always shown too, so an ambiguous name costs a highlight, never the explanation.
- **Canvas annotations are a frontend feature that grew a backend column.** The builder's grouping boxes and divider lines had nowhere to persist, which meant every save silently discarded them. They now round-trip through `PRODUCTION_TEMPLATE.annotations`; see the AS BUILT note in `30-BACKEND-PHASE2` Part C, including the client obligation to echo them back on a `PUT`.
- **Not browser-verified.** Every screen type-checks and Turbopack compiles the tree, and every endpoint behind them is smoke-verified server-side — but **no production screen has been driven in a browser and no end-to-end run has been walked through the UI.** Blocked on AuthHex being unable to issue a token (its configured MySQL host is unreachable). Tracked in `Frontend/PROGRESS.md §13`.
*End of 21-FRONTEND-PHASE2.md. Contract: `30-BACKEND-PHASE2.md`. Record work: `Frontend/PROGRESS.md`.*