Newer
Older
client_dwatch / docs / handoff / 指示書BE_エラー一覧でエラー内容を選ぶ画面_20261027.md

指示書BE — エラー一覧でエラー内容を選ぶ(画面)(2026-10-27)

担当の目安: Cursor でよい。ただし§5(3画面で共通の下書き)と§6(保存前の検証)は取りこぼしやすいので、 Grok / Composer で進めるなら、§9 の画面確認を省略しないこと。

前提: 指示書BD(API)の実装が develop に入っていること。

  • GET /api/error-lists
  • ErrorReportItem の error_list_id / error_list / error_list_selectable
  • PATCH /api/errors/{error_id}/status の error_list_id

契約の正は d-watch-api.yaml。決定の記録は D-185(案は §11)。


0. ユーザーの要望と決定(2026-10-27)

  • エラー情報を統計的に取るため、エラー一覧(/errors)でエラー内容を選択リストから選べるようにする
    • 選択肢は error_lists(決定稿 131件)
    • 分類(大分類。日本語は「清掃ロボットエラー」など5種)で絞れる
  • 見た目の参考は https://copipe-de-ui.net/examples/data-list-2 。次の要素で、条件はすべて同時に効く(AND):
    • キーワード入力
    • 分類の select
    • 件数「n件 / 全体」
    • 0件のときの文言
    • クリアボタン
  • 保存するのは id(error_list_id)
  • 選べる行の範囲:
    • 清掃開始失敗(NOT_STARTED)の行でも選べる
    • 制御盤(D-S_CONTROL_FAILURE)とセキュリティ(SECURITY_ON)の行では選べない
    • ★画面は error_type を見ない。error_list_selectable だけを見る★
  • 「その他」(note_required: true の選択肢。いまは id 131)を選んだら、備考の入力を必須にする
  • 未選択は空欄。★選んだあとに「未選択」へ戻す操作は作らない★(API が受け付けない)

1. 共通ルール(G1〜G4)

  • G1: 文言を足したら lib/i18n.ts に JP と EN の両方を入れる
  • G2: 触った箇所の古いコメントを直す
    • 例: ErrorsScreen.tsx:45 の「新しい共通部品は作らない」。今回の選択用の窓は、この画面専用の部品として作る。共通部品にはしない
  • G3: 素通りするテストを書かない
  • G4: 完了時に npx tsc --noEmit・npm run lint・npx vitest run の件数と、§9 の画面確認の結果を報告する

2. 禁止・罠

  • ★エラー内容の文字列(分類名・詳細)を t() に通さない★
    • 辞書に同じ綴りのキーがあり、誤訳される。例: 「該当するエラーはありません」「清掃中」「清掃キャンセル」「設定」(lib/i18n.ts)
    • JP / EN の切り替えは、useTheme() の lang を見て _ja / _en の列を出し分ける
  • ★その他(id 131)のセルに詳細「該当するエラーはありません」を出さない★
    • 一覧が0件のときの文言(ErrorsScreen.tsx:534)と同じ文字列で紛らわしい
    • 英語の詳細は "There are no errors." で、意味も逆になる
    • note_required の選択肢は、セルに大分類名(「その他(該当なし)」/ "OTHER ERROR")を出す
  • ★列を足さない★ ★2026-10-27 のユーザー指示で取り消し★(エラー内容は「詳細」の列として備考の左に出す。§12)
    • 表の最小幅は 1130px(ErrorsScreen.module.css:254)。列を足すと、D-180 項10 で直した横スクロールが戻る → ★横スクロールを出さない条件(1440×900 で表の最小幅 ≦ 約1130px)は今も守る★。§12 の列幅で ≒1129px
    • 「種類」のセルの2行目に入れる。COLUMN_COUNT = 8 と <colgroup> は変えない → COLUMN_COUNT = 9
  • ★セルの中にパネルを浮かせない★
    • 表は .tableWrap { overflow-x: auto } と .card { overflow: hidden } の中にあるので、浮かせたパネルは端で切れる
    • 選ぶ操作は窓(モーダル)で行う。作法は components/features/detail-log/AreaModal.tsx(role="dialog"・aria-modal・Escape で閉じる)
  • アラート(/alerts)と D-Sec アラート(/ds-alerts)の画面から error_list_id を送る経路を作らない(§5)
  • git add -A 禁止
  • docs/decisions.md に他人の未コミット差分があれば、上書きせずに止まる

3. 表示(通常モード・編集モード共通)

「種類」のセルを2行にする。

  • 1行目: error_type(今のまま。生値)
  • 2行目: エラー内容。小さめ・薄めの文字、1行、はみ出しは「…」、全文は title

2行目に出す文字(★判定は1つの純粋関数にまとめ、テストする★。例 lib/errors/error-content-label.ts):

行の状態 出す文字 title
error_list_selectable が false - なし
error_list_id が null 「未選択」(t() を通す。薄い色) なし
error_list_id はあるが error_list が null 「不明({id})」(t() を通し、{id} を置き換える) なし
error_list.note_required が true 大分類名(error_category_main_ja / _en) 「大分類 / 小分類」
それ以外 詳細(error_detail_ja / _en) 「大分類 / 小分類 / 詳細」
  • 編集モードで下書きがあるときは、下書きの id を GET /error-lists の選択肢から引いて同じ規則で出す
  • 下書きの id が選択肢に無いことは起きない(窓で選んだ id だけが入る)

4. 編集モードの操作

  • 選べる行(error_list_selectable が true)では、2行目を★固定幅のボタン★にする
    • ボタンの文字は §3 の表どおり。未選択なら「選択」
    • 押すと選択用の窓が開く
    • 保存中(saving)は押せない
  • 選べない行は - のまま(ボタンを置かない)
  • 選択肢(GET /api/error-lists)の取り方:
    • 編集モードに初めて入ったときに1回だけ取り、画面の state に持つ
    • 取得中は、ボタンを押せなくする
    • 失敗したら、編集中の案内(.editNotice)に「エラー内容の選択肢を読み込めませんでした」を出し、ボタンを押せなくする
    • 対応状況と備考の編集はそのまま使える
    • 取得の取り消しと、古い応答を捨てる処理は、この画面の既存の書き方(D-183)に合わせる

4-1. 選択用の窓(この画面専用の部品。例 components/features/errors/ErrorContentPicker.tsx)

窓の作り

  • role="dialog"・aria-modal="true"・aria-labelledby(見出し「エラー内容を選ぶ」)
  • 開いたらキーワード入力にフォーカスを移す。閉じたら、押したボタンにフォーカスを戻す
  • Escape・背景のクリック・「閉じる」ボタンで、何も変えずに閉じる
    • 「閉じる」は、画面の「キャンセル」とは役目が違うので文言を分ける

上段

  • 分類の select: 「すべての分類」と、選択肢の大分類を id 順に初めて出た順で並べる(_ja / _en。t() に通さない)
  • キーワード入力: 部分一致・大文字小文字を区別しない
    • 対象は、今の言語の詳細・小分類・大分類と、もう一方の言語の詳細
  • 件数: 「{n}件 / {total}件」
  • クリアボタン: キーワードと分類を初期状態に戻す
  • 上段の要素は固定幅にする(文言の長さで動かない)

下段の一覧

  • 縦スクロールする固定の高さにする
  • 見出しは「大分類 / 小分類」
    • 小分類の名前(データ・状態 など)は複数の大分類にまたがるので、小分類だけを見出しにしない
  • 項目は <button>。文字は詳細(_ja / _en)
  • note_required の項目には「備考必須」の小さな印を付ける
  • 今の値(下書き優先)の項目は強調し、aria-current="true" を付ける
  • 項目を押すと、下書きに id を入れて窓を閉じる
    • 行の値と同じ id を選んだら、下書きからその項目を消す(§5)
  • 0件のときは「該当するエラー内容がありません」
    • 画面の「該当するエラーはありません」とは別の文言にする
  • 並びは id 順(API の順のまま)

表示の作法

  • テーマのトークン(ダーク/ライト)に合わせる
  • 狭い画面では幅いっぱいにする
  • 日本語フォントのスタックを外さない(app/globals.css)

5. 下書き(★3画面で共通の仕組みを壊さない★)

lib/prechecks/edit-drafts.ts は、エラー一覧・アラート・D-Sec アラートの3画面が使っている。期待値表は P / D / S / H。

  • 共通側の ResolutionDraft と patchRequestFromDraft() の型と挙動は変えない
    • 返すのは PrecheckStatusUpdateRequest のまま
    • アラートと D-Sec が error_list_id を送らないことを、既存のテストが守っている
  • エラー一覧用に lib/errors/edit-drafts.ts を作る
    • ErrorResolutionDraft = ResolutionDraft & { error_list_id?: number }
    • 正規化: 行の error_list_id と同じなら消す。項目が0になったらエントリごと消す
    • 本文を作る関数は ErrorStatusUpdateRequest を返す
      • 対応状況は必須(今の規則のまま)
      • 備考は変えたときだけ入れる
      • error_list_id も変えたときだけ入れる
    • ★エラー内容だけを変えた行も「変えた行」に数える★。保存(N件)の N に入り、送られること
    • 共通側の saveDrafts() は、型引数で下書きと本文の型を受け取れるように広げてよい
      • 3画面の挙動は変えない
      • 広げずに、エラー一覧用の関数を lib/errors/edit-drafts.ts に作ってもよい
  • 期待値表 E を lib/errors/__tests__/edit-drafts.test.ts に作る。最低限、次の行を入れる:
    • エラー内容だけ変更
    • 行と同じ id に戻す(下書きが消える)
    • 対応状況と同時に変更
    • 備考と同時に変更
    • 下書きなし
  • 変えた行の目印(rowChanged)は、エラー内容の変更でも付くこと

6. 保存前の検証(その他は備考必須)

  • 「保存」を押したら、送る前に行ごとに確かめる。条件は次のすべて:
    • その行の下書きが★エラー内容か備考を変えている★(対応状況だけの変更には掛けない。サーバーの規則と同じ。指示書BD §3-3 の手順4)
    • その行の★適用後★のエラー内容(下書き優先)が note_required
    • ★適用後★の備考(下書き優先)を前後の空白を除くと空
  • 当たった行は★送らない★
    • 下書きを残す
    • 行の直後のエラー(rowErrors)に「その他を選んだときは備考を入力してください」を出す
    • 「{n}件を保存できませんでした」の件数に入れる
    • ほかの行は送る(1行の失敗で止めない。今の規則のまま)
  • 既にその他が入っている行で備考を消した場合も対象に入る(サーバーが 422「備考を入力してください」を返すため)
  • ★既にその他で備考が空の行(アラート画面で備考を消した等)で、対応状況だけを変えた場合は送る★(サーバーも通す)
  • 編集中の見た目:
    • note_required の行は、備考欄の placeholder を「備考を入力(必須)」にする
    • 空のあいだは枠を警告色にする
  • サーバーの 422(どの理由でも)は、今のとおり行の直後に文言を出す。最後の砦として残る

7. 触るファイル(目安)

  • components/features/errors/ErrorsScreen.tsx / ErrorsScreen.module.css
  • 新規
    • components/features/errors/ErrorContentPicker.tsx(+ CSS)
    • lib/errors/edit-drafts.ts
    • lib/errors/error-content-label.ts(§3)
    • それぞれのテスト
  • lib/prechecks/edit-drafts.ts: 型引数を広げる場合だけ
  • lib/i18n.ts
  • docs/code-map.md(画面の表と「エラー一覧」の節)、docs/spec.md(該当の機能の行があれば)

8. テスト

  • §3 の表のすべての行(JP / EN)
  • §5 の期待値表 E と、既存の P / D / S / H が通ること
  • §6:
    • その他+備考なし → 送らない
    • その他+空白だけ → 送らない
    • その他+備考あり → 送る
    • 既にその他の行で備考を消す → 送らない
    • 既にその他で備考が空の行で、対応状況だけ変える → 送る
    • 当たった行があっても、ほかの行は送る
  • キーワードと分類の絞り込みの純粋関数(AND・大文字小文字・0件・クリア)

9. 画面で確かめる(fixtures)

手順:

  1. USE_FIXTURES=true npm run build
  2. USE_FIXTURES=true npx next start -p 3100
  3. アプリ内ブラウザで開く(Edge は使わない)

★:3000 のサーバーは止めない★。

fixtures の行:

行 期待
FX-ERR-01 「非常停止スイッチが作動しました」
FX-ERR-05 「不明(9999)」
FX-ERR-11(清掃開始失敗・その他) 「その他(該当なし)」
D-S_CONTROL_FAILURE / SECURITY_ON の行 -
ほか 「未選択」

確かめること:

  • 通常モードと編集モードの表示、JP / EN、ダーク/ライト
  • 画面幅 1440px で横スクロールが出ないこと(いちばん長い英語の詳細を選んだ行でも)
  • 窓の操作
    • キーボードだけで、開く → 絞る → 選ぶ → 閉じる(フォーカスが戻る)ができること
    • Escape で閉じること
  • 保存の流れ
    • 有効な id を選んで保存すると、反映して通常モードに戻ること
    • その他を選んで備考を空のまま保存すると、送られずに行エラーが出ること
    • 備考を入れて保存し直すと、成功すること
  • アラート・D-Sec アラートの画面の編集が今までどおり動くこと

ローカルDB(dwatch_local)で実DBモードを確かめるなら:

  • 先に scripts/local-db/ の手順で error_lists の表と error_list_id の列を入れる
  • シードを入れ直す

10. 今回やらないこと

  • 統計の画面・CSV 出力
  • エラー内容での絞り込み
  • 未選択に戻す操作
  • アラート・D-Sec アラートの画面への追加

11. D-185(案)

  • エラー一覧の「種類」のセルの2行目にエラー内容を出す(列は足さない)
  • 編集モードでは、ボタン → 窓(キーワード・分類・件数・クリア)で選ぶ
  • その他は、セルに大分類名を出す。保存前に備考が必須かを確かめる
  • 文字列は t() に通さず、言語で列を出し分ける
  • 却下案:
    • セルの中に浮かせるパネル: 表の端で切れる
    • 2段の <select> やグループ分けしたネイティブの select: キーワードで絞れない。分類で絞っても「清掃ロボット状態エラー」だけで51件残る
    • 新しい列: 横スクロールが戻る

12. 変更(2026-10-27 ユーザー指示)

  • ★エラー内容は「種類」のセルの2行目ではなく、独立した列「詳細」(EN "Details")として★備考のすぐ左★に出す★
    • 列の並び: 検知時刻・施設名・シリアル・種類・レベル・紐づく予定・対応状況・★詳細★・備考(COLUMN_COUNT = 9)
    • 「種類」のセルは error_type だけに戻す
    • 詳細のセルに出す文字・title は §3 の表のまま(errorContentView()。t() に通さない)。「-」「未選択」「不明(id)」は薄い色
    • 編集モードのボタン(§4)は詳細のセルに置く。★列幅いっぱい・左寄せ★。押したときの動き・フォーカスの戻し方は変えない
  • ★長い文字は折り返す(「…」で切らない・1行に詰めない)★ — 通常モードも編集モードのボタンも同じ。 英語や識別子のような長い綴りは overflow-wrap: anywhere で列の中で切る
    • §3 の「1行、はみ出しは「…」」と §4 の「固定幅のボタン」はこの指示で置き換え
  • 列幅(ErrorsScreen.module.css の .table。値は言語によらず同じ。文字幅は 2026-10-27 にブラウザで測った値):| 列 | 幅 | 旧 | |---|---|---| | 検知時刻 | 120px | 120px | | 施設名 | 表の 9%(最小幅のとき ≒102px) | 残りの半分(最小 150px) | | シリアル | 140px(「8111R5628060033」≒111px + padding 24) | 160px | | 種類 | 表の 9%(最小幅のとき ≒102px) | 残りの半分(最小 150px) | | レベル | 90px | 90px | | 紐づく予定 | 110px(「事前チェック」/「(30分前)」の2行) | 150px | | 対応状況 | 150px(D-165。変えない) | 150px | | ★詳細★ | ★残りの幅すべて(最小幅のとき 196px 以上)★ | (なし) | | 備考 | 120px | 160px |
    • ★表の最小幅 = (固定列の合計 730 + 詳細の最小 196) ÷ (1 − 0.18) ≒ 1129px★(旧 1130px)。 1440×900(縦スクロールバーありで表の入れ物 ≒1139px)で横スクロールが出ない(D-180 項10 を保つ)
    • 施設名・種類を割合にしたのは、広い画面で詳細だけでなく両列も少し広がるようにするため (却下: 3列で等分 → 狭い画面で詳細が ≒133px/詳細を 200px 固定 → 広い画面で種類の列だけが空いて広がる)
    • 施設名は最小幅のとき中が ≒78px なので、長い店名は2〜3行に折り返す