busy-office-ui
Menu

Pattern: RF putaway

Who uses it: whoever moves what receiving just accepted into its storage bin — the second half of every inbound delivery. How often: per handling unit, all day while trucks unload. What "done" looks like: the HU physically in the bin the system chose, proven by scanning that bin's label — the verification idiom: the screen names the destination in bold, and the scan must match it. A wrong bin flashes red before the pallet is set down, which is precisely when a putaway error is still free to fix. Not the receiving step — goods receipt captures what ARRIVED against the PO; putaway moves what receipt accepted. Merging them makes dock workers do binning decisions with a truck waiting, which is how both jobs end up done badly.

The screen — try it

Shown the way the scanner gets it: an isolated 360×640 document styled by ONLY the rf-essentials profile (Chrome/WebView 108) — not the docs bundle. One screen, not two: this page used to render the same component a second time at desktop width, which showed the reader a size no putaway worker ever holds (roadmap 131.1).

Scan (type + Enter) B-07-12 to confirm — anything else flashes the error verdict with the expected bin in the announcement. The compare is three consumer lines on bo:scan: verification is validation, and validation is yours; the framework paints capture and verdict.

Markup

<!-- The verification idiom: the system NAMES the destination and
     the scan must MATCH it. Matching is three consumer lines — the
     framework paints capture and verdict, never decides them. -->
<dl class="bo-kv bo-kv--rows"> …HU / From / To bin (bold — the one fact that matters)… </dl>

<input class="bo-input bo-input--code" data-scan-input data-scan-flash
    aria-describedby="scan-status" />
<p id="scan-status" data-scan-status aria-live="polite" class="bo-visually-hidden"></p>

<script type="module">
  document.addEventListener("bo:scan", (e) => {
    if (e.detail.value !== expectedBin) flashScanResult("error", "Wrong bin: …");
    else confirmPutaway();
  });
</script>

<div class="bo-btn-group bo-btn-group--bar">
  <button class="bo-btn bo-btn--secondary">Back</button>
  <button class="bo-btn bo-btn--secondary">Bin full</button>
  <button class="bo-btn bo-btn--secondary">Wrong HU</button>
</div>

Anatomy

  1. Move header — KV rows: HU, where it came from, and the destination in bold — the one fact the whole screen exists to confirm.
  2. Verification scan — scan input + flash; the label says what to scan, the announcement carries the verdict.
  3. Confirmation line — a badge + text, revealed on match; the next HU replaces the screen in a real app.
  4. Exception bar — Back / Bin full / Wrong HU: the two real exceptions this move has, intent-named.

Data contract

RequestReturns / on failure
GET /rf/putaway/nextthe next HU + its system-chosen bin, server-rendered
POST /rf/putaway/:hu/confirmthe scanned bin; the server RE-CHECKS the match (the client compare is UX, never the control) and returns the next HU
POST … bin-fullreturns an alternative bin for the same HU — the worker never chooses a bin freehand
409 (bin occupied since assignment)a new destination with the reason — never a silent reroute; reroutes without reasons are audit holes

States

StateWhat ships
Awaiting verificationdestination bold in the header; the field names it again in its label
Wrong bin scannederror flash + expected bin announced; field clears for retry — the pallet is still on the truck of the forklift, where the fix is free
Confirmedbadge + text, then the next HU; the flash alone is never the record
Bin fullthe exception button, not a freehand override — the server picks the alternative
Session lostreconnect re-renders the HU as the server has it; persistence is infrastructure, not a screen promise
No JSEnter posts the scan; the server's re-render carries the verdict. Header, field and bar are markup

Components used

complexity 2 of 4

↑↓ move Enter open Esc close