AI に出荷梱包を作らせてみた — 3つの画面が同じ2段スキャンを手組みしたので、ScanGate を作った(やってみた #49)
/shipping375px のビューポートで撮影。縦長のページはフレーム内をスクロールします。
解説記事
AI に出荷梱包を作らせてみた — 3つの画面が同じ2段スキャンを手組みしたので、ScanGate を作った(やってみた #49)
やってみたシリーズ: 自作のデザインシステム
@gunjo/ui(群青)を、群青のことを一度も見たことのない AI に、実際の画面で組ませていく連載。物流/倉庫(WMS)3枚目——出荷検品 / 梱包(出荷照合 × カートン→商品の2段スキャン × カートン管理/重量 × 最終検品 × 配送ラベル)。
#47 ピッキング(取り出す)・#48 入庫(格納する)に続く出荷側。ピッキング済みを箱詰め前に照合し、カートン(箱)をスキャンして開き → 商品をスキャンして詰める——カートン→商品の2段スキャン。出荷の作法。
結果 — 4.5/5(tsc/build 緑・全コンポーネント採用)
tsc/build 緑・console 0・375px ハンディプライマリ・カートンスキャン→開封→商品スキャンで梱包+1・カートン未開封で商品スキャンは「先にカートンを」プロンプト・過剰梱包阻止・重量/配送業者ゲート・出荷確定で read-only サマリ。
※ 今回は予備知識ゼロの AI の最終レポートがセッションの制限で返らなかったが、退避した画面コードを検証して主要なシグナルを確認した(tsc/build 緑・全コンポーネント採用・
scanStageの状態管理を26箇所で手組み)。
今回の本題 — 3画面が同じ2段スキャンを手組みした → ScanGate を作る
物流の3画面すべてが、同じ2段スキャンのゲートを、scanStage の状態管理で手組みしていました。しかも順序がバラバラなのに同じ形:
#47 ピッキング: location → item(棚を確認してから商品)
#48 入庫 : item → location(商品を読んで推奨棚へ・逆順)
#49 出荷梱包 : carton → item(箱を開いてから商品を詰める)
順序が3通り違っても、本質は同じ——「スキャンして確定(引き継ぎ値)→ 次の段がその値を読んでスキャンしてカウント」。毎回 scanStage/activeCartonId を手で書き、段ごとに ScanInput を key で remount し、自動フォーカスを配線していました。3回ルール発火で ScanGate(#227)(PR #232)を出荷:
<ScanGate
stages={[
{ id: "carton", label: "① カートンをスキャン", onScan: (code) => {
const c = openCarton(code)
return c ? { ok: true, message: `${code} を開きました`, advance: "next", value: c }
: { ok: false, message: "見つかりません", advance: "stay" }
}},
{ id: "item", label: "② 商品をスキャン", onScan: (code, ctx) => {
const line = pack(code, ctx.values.carton) // 前 stage の確定 context を読む
return line ? { ok: true, message: `${line.name} を梱包`, advance: "stay" }
: { ok: false, message: "受注に無い商品", advance: "stay" }
}},
]}
/>
- 各段の
onScan(code, ctx)が{ ok, message, advance, value }を返す。advance("next"/"stay"/"reset"/段の id)が状態遷移を駆動、valueは段の id で記憶され、次の段がctx.values[stageId]で読む。 - 段の間で自動で次へ進む+自動フォーカス(スキャンガンのループ)。最終段の
"next"は先頭に戻る+引き継ぎ値クリア=1サイクル完了。 - 番号付きの ステップ表示(done/active/todo)+命令的な
refハンドル(reset/goTo/getValues)で「カートンを閉じる」「次のロケーションへ」ボタン。 - 読み上げ・二重発火の抑制・履歴は
ScanInputから引き継ぐ。
ブラウザ実証: カートン CTN-001 をスキャン→ carton→item に進む+引き継ぎ値を保存→商品スキャンで「デスクライト LED を CTN-001 に梱包(1/3)」(引き継いだカートンを参照)→ stay で連続梱包→「閉じる」ボタンの ref.reset() で 段1+引き継ぎ値クリア。
複利も全部効いた
ScanGate の土台 ScanInput はもちろん、Delta・EditableDataTable・RevealSection・Checkbox(最終検品のチェックリスト)・Progress の tone(前回 #48 で追加)・CardTitle as——全部発掘・採用。前々回・前回の投資が、出荷画面でも返ってきました。
学び — 「順序が違っても同じ形」は、合成コンポーネントのサイン
location→item / item→location / carton→item = 順序は違うが同じ2段ゲート
→ 抽象は「段の配列+各段の解決処理+確定した引き継ぎ値+自動で次へ」
3画面が表面上は違う作法(ピッキング/入庫/出荷)なのに、同じ合成の構造を手組みしていました。これこそ「上位でまとめるコンポーネント」が隠れているサイン——個別のつなぎのコードが3回、形を変えて再発したら、それは抽象を1つ作るべき合図です。ScanInput(単発スキャン)を3画面で作り切った上に、その段階の組み立てを ScanGate として括り出しました。コンポーネントは2層になりました。
次回予告(やってみた #50)
- 在庫ロケーション管理 / 棚卸移動(容量 Meter#230 を2回目確認へ)。方針: 1業界3〜5枚・可能な限り違う内容・業界特有UI最優先。
試す
まだ alpha。3画面が同じ2段スキャンを手組みしたので、段階の組み立てを ScanGate として括り出した回。
この連載は、作者が AI(Claude と Codex)と協働で制作しています。実験・検証の設計、判断、公開前の事実確認は人間が行い、実作業と下書き執筆は AI が担っています。
使用した @gunjo/ui コンポーネント
この画面のソースが直接 import しているコンポーネントです。
cold AI が組み上げた実コード
ファイル名をクリックでソースを展開できます。