ハコヅミを作る #18:まだ積んでいない項目を目立たせる/ブログの画像で見つけた不具合
前回(#17)で開発の流れを整えたので、機能の話に戻ります。今回は3つです。
- 持ち物チェックで、まだ積み込んでいない項目を目立たせる(#83)
- ブログ用の画面を撮っていて見つけた、一覧の進み具合の不具合(#93)
- その不具合をきっかけに決め直した、ボードの「完了」の意味
まだ積んでいない項目を目立たせる(#83)
持ち物チェックは、チェック済みの項目も、まだの項目も、同じ見た目で並んでいました。#16で消耗品も入り、項目は45件ほどになります。積み込みの途中で画面を見ても、「あと何が残っているか」がすぐには分かりません。
そこで、3つのことをしました。

チェック済みは薄い文字と取り消し線。コンテナの見出しの右に、残りの数が出る
画面は、この記事の時点のコードを手元で動かして撮ったものです。ギアは#7で Notion から取り込んだ実際のもので、消耗品は記事用に登録したものです。
1. チェック済みを薄く、まだの項目を少し太く
チェック済みの項目は、薄い文字(ink-muted)と取り消し線にしました。まだの項目は、今の濃さのまま、少しだけ太くしています。
取り消し線を引くのは、名前だけです。数量(×2)、メーカーのバッジ、「残りなし」には引きません。「OD缶 110 は積んだけれど、残りがない」は、積んだあとでも読めてほしい情報だからです。
2. コンテナごとに「あと N」
コンテナ(と「コンテナなし」)の見出しの右に、残りの数を出します。0 になったら「積み込み済み」に変わります。
残りの数は、新しく数えているわけではありません。進み具合(チェック済 / 対象)を出すために、#8で作った関数がすでにあるので、それを使って「対象 − チェック済」を出すだけです。
// 残りの数(#83)。今回不要でなく、まだチェックしていない項目の数(ギアと消耗品の両方)
export function remainingCount(items: CampItemState[]): number {
const { checked, target } = campProgress(items);
return target - checked;
}
見出しの右に物を置けるように、UI 部品の Section に trail(右端に置くもの)を足しました。左に置く lead(コンテナの一括チェックのチェックボックス)は、#9のときに作ったものです。見出し(h2)の外に置いているので、見出しの文字は変わらず、これまでの E2E もそのまま通りました。
3. 「まだのものだけ表示」
見出しの下のチェックボックスをオンにすると、チェック済みと今回不要の項目が隠れ、まだの項目だけが残ります。

「まだのものだけ表示」をオンにしたところ。全部済んだコンテナは、見出しごと消える
- 全部済んだコンテナは、見出しごと隠す
- オンのまま項目をチェックすると、その場で消えていく
- すべて済んだら「すべて積み込み済みです」と出す
- 切り替えは保存しない(ページを開き直すとオフに戻る)
切り替えの状態を保存する(次に開いたときも続ける)ことは、今回は対象外にしました。まずは使ってみて、要るかどうかを決めます。画面の中だけの状態なので、Svelte の $state と $derived だけで済みました。
let onlyRemaining = $state(false);
const groups = $derived(onlyRemaining ? remainingGroups(data.groups) : data.groups);
絞り込みの処理(remainingGroups())は、ほかのロジックと同じく src/lib/camps.ts に切り出して、ユニットテストしています。
ブログの画面を撮っていて、不具合を見つけた(#93)
#15の記事のために、キャンプの一覧とキャンプの画面を撮っていたときのことです。2枚を見比べて、数字が合わないことに気づきました。
| 画面 | 進み具合 |
|---|---|
| キャンプの画面 | 15 / 45 |
| キャンプの一覧 | 12 / 36 |
同じキャンプなのに、一覧のほうが少ない。差の9件は、消耗品の数でした。
原因は、一覧の進み具合を数える処理です。ギアの項目(camp_items)だけを集計していて、#16で足した消耗品の項目(camp_supply_items)が入っていませんでした。消耗品を持ち物チェックに入れた Issue の AC は、キャンプの画面の進み具合だけを書いていたので、テストでは見つかりませんでした。
(#15 の記事の一覧の画像には、この「12 / 36」がそのまま写っています。記録として、差し替えずに残しています。)
直し方:join で足さない
直すときに気をつけたのは、2つのテーブルを同じクエリで join しないことです。
キャンプに、ギアの項目と消耗品の項目を両方 join すると、行が掛け合わされます。ギア2件・消耗品3件なら 2 × 3 = 6 行になり、数え方によっては数がずれます。テーブルごとに数えてから、足すようにしました。
const gearTarget = sql<number>`(select count(*) from ${campItems} where ${campItems.campId} = ${camps.id} and ${campItems.notNeeded} = 0)`;
const supplyTarget = sql<number>`(select count(*) from ${campSupplyItems} where ${campSupplyItems.campId} = ${camps.id} and ${campSupplyItems.notNeeded} = 0)`;
// ...
target: sql<number>`${gearTarget} + ${supplyTarget}`
テストも、ギア2件・消耗品3件という「掛け合わせると数が変わる」形で書いています。足したテストが、直す前のコードでは落ちることも確かめました。
自分のデータを入れて、画面を並べて眺める。ブログのための作業でしたが、結果として、受入確認とは別の目で見るレビューになっていました。
「完了」は、本番に出たときにする
この不具合を直して、開発環境で受入確認を済ませたあと、Claude Code は Issue を閉じて、#17で作ったボードを「完了」にしました。そこで引っかかりました。
今回の対応って、Projects が完了になっているけど、本番待ちではないの?
本番では、まだ直っていません。本番で起きている不具合が、ボードでは「完了」になっている。これは誤解のもとです。
それまでの決まりは、こうでした。
- 「本番待ち」にするのは、本番でしか確かめられない AC が残っているときだけ(テーブルを変える Issue の「本番の D1 に〜ができる」など)
- それ以外は、開発環境での受入が済んだら閉じて「完了」
決まりどおりに動いた結果ですが、「完了」が「本番に出ている」を意味していなかったわけです。次のように直しました。
| いつ | 状態 |
|---|---|
| 受入 OK のあと、本番リリースを待っているとき(アプリの動きを変える Issue はすべて) | 本番待ち |
| 本番リリースのデプロイを確かめて、Issue を閉じたとき | 完了 |
CI や CLAUDE.md だけを変える Issue は、アプリが変わらないので、これまでどおり受入 OK で閉じます。
こうすると、ボードの「本番待ち」の列が、そのまま次の本番リリースに入るものの一覧になります。リリースの PR の本文も、そこから作れるようになりました。実際、次のリリースでは「本番待ち」に5つ並び、それをそのまま PR に書いています。
決まりを作ったときには気づかなかった穴が、実際の Issue を流してみて見つかる。CLAUDE.md とスキルは、こうやって少しずつ直していくものだと思っています。
おわりに
- チェック済みは薄く、コンテナごとに「あと N」、まだのものだけの表示。残りの数は、今ある進み具合の関数から出す
- 2つのテーブルを join して数えると、行が掛け合わされる。テーブルごとに数えてから足す
- ブログ用に画面を並べて見ることが、レビューになった
- ボードの「完了」は、本番に出たときにする。「本番待ち」の列が、次のリリースの一覧になる
次回は、トップページにタイトルバナーとロゴを入れた話です。