diff --git a/CLAUDE.md b/CLAUDE.md index 1e22e52..773b007 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -40,11 +40,11 @@ ## References -| ファイル | 内容 | -| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `docs/requirements.md` | 要件概要(目的・スコープ・方針) | -| `docs/data-model.md` | データモデルに関するコンテクスト(メタデータ) | -| `docs/adr.md` | 意思決定記録(なぜその選択か) | -| `docs/roadmap.md` | ロードマップ+マイルストーン | -| `docs/ui-mockups/` | MVP の UI モック 9 枚(=「UI をモックに合わせる」の完了条件)。`assets/` はモックが使う SVG で**設計資料**(ここから import しない。実装時に `src/` へ移す) | -| `CONTRIBUTING.md` | 開発フロー・コードスタイル・PR | +| ファイル | 内容 | +| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `docs/requirements.md` | 要件概要(目的・スコープ・方針) | +| `docs/data-model.md` | データモデルに関するコンテクスト(メタデータ) | +| `docs/adr.md` | 意思決定記録(なぜその選択か) | +| `docs/roadmap.md` | ロードマップ+マイルストーン | +| `docs/ui-mockups/` | MVP の UI モック 10 枚(=「UI をモックに合わせる」の完了条件)。`assets/` はモックが使う SVG で**設計資料**(ここから import しない。実装時に `src/` へ移す) | +| `CONTRIBUTING.md` | 開発フロー・コードスタイル・PR | diff --git a/docs/data-model.md b/docs/data-model.md index 9755f84..61637bf 100644 --- a/docs/data-model.md +++ b/docs/data-model.md @@ -40,9 +40,9 @@ 同じ物を 2 回登録できないようにしたい。ただし `name`・`age`・`caskType`・`isLimited` の 4 列に一意制約を張っても効かない。**Postgres は NULL 同士を別の値として扱う**ため、年数も樽も空の「山崎」は何行でも登録できてしまう。 -そこで 4 項目を**区切り文字でつないだ**文字列(`山崎|12|シェリー|0`)を別の列に持ち、**`userId` との複合で一意制約を張る**。空欄は空文字になるので NULL が消え、比較が効く。 +そこで 4 項目を**区切り文字でつないだ**文字列(銘柄名␟年数␟樽␟限定版)を別の列に持ち、**`userId` との複合で一意制約を張る**。つなぐ前に NFKC 変換・空白除去(**内部の空白も含めて全て**)・小文字化を掛けて表記ゆれを吸収し、空欄は空文字にする。NULL が消えるので比較が効く。 -区切り文字を省くと、銘柄名「山崎12」+年数なしと、銘柄名「山崎」+12 年が同じ文字列になる。`userId` を外すと、誰かが登録済みのボトルを他の人が登録できなくなる。 +区切り文字を省くと、銘柄名「山崎12」+年数なしと、銘柄名「山崎」+12 年が同じ文字列になる。ただし**区切りには入力に現れない文字を使い、その文字が来たら共有スキーマで弾く**。銘柄名と樽は自由入力で、API には UI を通さずに送れるため、「入力に現れないはず」という前提だけでは衝突を防げない。`userId` を外すと、誰かが登録済みのボトルを他の人が登録できなくなる。 ### Prisma 6 → 7 で変わったこと diff --git a/docs/requirements.md b/docs/requirements.md index 7a2f6c6..65806de 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -55,9 +55,9 @@ - **記録項目**(リッチだが必須は最小) - 銘柄名 …**必須** - 産地(固定リスト選択式)/ 地域 / 年数(NAS 可)/ 樽 / 限定版 / 本数(既定 1)/ メモ …**すべて任意** -- **重複ボトルの検知**:**銘柄名・年数・樽・限定版**が同じなら同じボトルとみなす(産地・地域・メモ・本数は含めない)。表記ゆれは NFKC 変換と空白除去で吸収し、判定キーを列に持って **`userId` との複合一意制約**で保証する(ユーザーをまたいだ一意にはしない)。登録時に検知したら**エラーを出し、既存のボトルへのリンクを見せる**(本数の加算は詳細画面から行う) +- **重複ボトルの検知**:**銘柄名・年数・樽・限定版**が同じなら同じボトルとみなす(産地・地域・メモ・本数は含めない)。表記ゆれは NFKC 変換・空白除去・小文字化で吸収し、判定キーを列に持って **`userId` との複合一意制約**で保証する(ユーザーをまたいだ一意にはしない)。**登録・編集のどちらでも検知**し、既存のボトルを示すトーストを出して**詳細へ辿れるようにする**(その場で本数は加算しない) - **傾向(簡単な可視化)**:総本数・銘柄数・産地数のサマリ/産地別の本数/限定版の割合。**銘柄数の数え方は 3 章の用語定義に従う**(表記ゆれで水増ししないため)。産地が未設定のボトルは「未設定」としてグラフに出す(除外しない) -- **UI をモックに合わせる**:完了条件は `docs/ui-mockups/` の 9 枚に一致すること +- **UI をモックに合わせる**:完了条件は `docs/ui-mockups/` の 10 枚に一致すること - 下部タブバー(コレクション・傾向・登録・アカウント)※ナビゲーションは `layout.tsx` に置き、各ページは自分の幅を持たない(後のレスポンシブ対応を layout 1 枚に閉じるため) - 一覧の 2 カラムカードグリッド。カードに出すのは銘柄名・産地・年数・本数・限定版バッジ - 0 件のときは空状態を出す @@ -74,27 +74,27 @@ ### 保留 -| 項目 | 理由 | 見直しのトリガー | -| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| 分類(モルト/グレーン/ブレンデッド)の項目追加 | 材料不足:一覧の検索・フィルタ・並べ替え・ページネーションや傾向ページの方針をもっと知りたいため。 | 一覧の検索・フィルタ・並べ替え・ページネーションや傾向ページの実装の後 | -| 蒸留所の項目追加 | 材料不足:フィルタ軸にするなら入力補完とセットにしないと表記ゆれが起こる | わからない | -| 参照テーブル+コンボボックス(産地・蒸留所の選択肢をユーザーが足せるようにする) | 材料不足:MVP は固定リストで足りている。リストに無い産地を登録したい場面がまだ実際に出ていない | 固定リストに無い産地・蒸留所を登録したくなったとき・蒸留所の項目追加に着手するとき | -| 重複時に「本数を足しますか?」と確認するダイアログ | 材料不足:MVP はエラー+既存へのリンクで成立する。詳細画面から加算する手数を父が不便と感じるかが未検証 | 父のフィードバック・重複登録が実際に頻発したとき | -| 過去の入力からのオートコンプリート | 材料不足:入力の手間が実際に問題になるかが未検証 | 登録件数が増えて入力が苦になったとき | -| ウイスキーデータセットの調査と整備 | 材料不足:はじめにやるべきじゃなく固まってからやるべきなため | ウイスキーのデータが欲しくなったとき | -| バーコードスキャンからの入力補完 | 前提不足:iOS で動く読み取り手段がない+バーコード索引のデータセットがあるのか不明(むずそうなため!) | ウイスキーデータセットの調査でデータセットが見つかったとき・準備できた時 | -| ラベル写真から AI 自動入力 | 前提不足:**実現可能性が未検証**。写真アップロードが先に要る | 写真が入り、実データで精度を試せるようになったときやウイスキーデータセットの調査でデータセットが見つかったとき・準備できた時 | -| 価格の外部 API 取得 | 前提不足:用途が具体化していない | 使い道が具体化したとき・ウイスキーデータセットの調査でデータセットが見つかったとき・準備できた時 | -| 購入額の記録と月次・年次グラフ | 前提不足:**購入履歴のデータモデル判断が先に要る**(実装が大変そうだから) | 方針が決まったら | -| テイスティングノート | 材料不足:別テーブルが必要そうで実装が広範囲で内容も決まっていないため | 方針が決まった時 | -| ウィッシュリスト | 材料不足:いるか不明なため | 父のフィードバック・必要だと判断した時 | -| コレクション実績バッジ | 材料不足:父が喜ぶか不明。ソシャゲのようで安っぽくなる懸念もある | 父のフィードバック | -| スコッチ/アイリッシュ等への呼称変換・ニューワールド集約 | 材料不足:モックの傾向ページは産地名をそのまま出しており、変換した呼称の置き場がない(ADR-0009 のこの部分は未実装) | 呼称や集約が実際に欲しくなったとき | -| NAS 表示(年数が空のとき「NAS」と出す) | 材料不足:父が年数をどれだけ入力するか未検証。空欄が NAS なのか入力漏れなのかを実データで見たい | 父のフィードバック・実データで空欄の割合が分かったとき | -| 傾向での NAS の割合 | 材料不足:上の NAS 表示を入れてからでないと判断できない | NAS 表示を入れた後 | -| フィルタ・並べ替えの強化(複数軸ファセット) | 前提不足:まず採用の単一軸フィルタを入れてから | 単一軸では足りないと分かったとき | -| あいまい検索 | 材料不足:数百件では部分一致で足りる。Prisma からは raw SQL が要る | 誤字で見つからない事例が実際に出たとき | -| 無限スクロール | 材料不足:難易度の割にインパクトは少なそうなため! | 件数が増えて一覧の描画が重くなったとき・ほぼ完成して時間に余裕がある時 | +| 項目 | 理由 | 見直しのトリガー | +| -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| 分類(モルト/グレーン/ブレンデッド)の項目追加 | 材料不足:一覧の検索・フィルタ・並べ替え・ページネーションや傾向ページの方針をもっと知りたいため。 | 一覧の検索・フィルタ・並べ替え・ページネーションや傾向ページの実装の後 | +| 蒸留所の項目追加 | 材料不足:フィルタ軸にするなら入力補完とセットにしないと表記ゆれが起こる | わからない | +| 参照テーブル+コンボボックス(産地・蒸留所の選択肢をユーザーが足せるようにする) | 材料不足:MVP は固定リストで足りている。リストに無い産地を登録したい場面がまだ実際に出ていない | 固定リストに無い産地・蒸留所を登録したくなったとき・蒸留所の項目追加に着手するとき | +| 重複時にその場で本数を加算する | 材料不足:MVP は既存ボトルを示して詳細へ辿れれば成立する。加算は編集画面から行えるが、その手数を父が不便と感じるかが未検証。編集で衝突した場合の加算は 2 行の統合にあたり、片方のメモ・産地が失われる | 父のフィードバック・重複登録が実際に頻発したとき | +| 過去の入力からのオートコンプリート | 材料不足:入力の手間が実際に問題になるかが未検証 | 登録件数が増えて入力が苦になったとき | +| ウイスキーデータセットの調査と整備 | 材料不足:はじめにやるべきじゃなく固まってからやるべきなため | ウイスキーのデータが欲しくなったとき | +| バーコードスキャンからの入力補完 | 前提不足:iOS で動く読み取り手段がない+バーコード索引のデータセットがあるのか不明(むずそうなため!) | ウイスキーデータセットの調査でデータセットが見つかったとき・準備できた時 | +| ラベル写真から AI 自動入力 | 前提不足:**実現可能性が未検証**。写真アップロードが先に要る | 写真が入り、実データで精度を試せるようになったときやウイスキーデータセットの調査でデータセットが見つかったとき・準備できた時 | +| 価格の外部 API 取得 | 前提不足:用途が具体化していない | 使い道が具体化したとき・ウイスキーデータセットの調査でデータセットが見つかったとき・準備できた時 | +| 購入額の記録と月次・年次グラフ | 前提不足:**購入履歴のデータモデル判断が先に要る**(実装が大変そうだから) | 方針が決まったら | +| テイスティングノート | 材料不足:別テーブルが必要そうで実装が広範囲で内容も決まっていないため | 方針が決まった時 | +| ウィッシュリスト | 材料不足:いるか不明なため | 父のフィードバック・必要だと判断した時 | +| コレクション実績バッジ | 材料不足:父が喜ぶか不明。ソシャゲのようで安っぽくなる懸念もある | 父のフィードバック | +| スコッチ/アイリッシュ等への呼称変換・ニューワールド集約 | 材料不足:モックの傾向ページは産地名をそのまま出しており、変換した呼称の置き場がない(ADR-0009 のこの部分は未実装) | 呼称や集約が実際に欲しくなったとき | +| NAS 表示(年数が空のとき「NAS」と出す) | 材料不足:父が年数をどれだけ入力するか未検証。空欄が NAS なのか入力漏れなのかを実データで見たい | 父のフィードバック・実データで空欄の割合が分かったとき | +| 傾向での NAS の割合 | 材料不足:上の NAS 表示を入れてからでないと判断できない | NAS 表示を入れた後 | +| フィルタ・並べ替えの強化(複数軸ファセット) | 前提不足:まず採用の単一軸フィルタを入れてから | 単一軸では足りないと分かったとき | +| あいまい検索 | 材料不足:数百件では部分一致で足りる。Prisma からは raw SQL が要る | 誤字で見つからない事例が実際に出たとき | +| 無限スクロール | 材料不足:難易度の割にインパクトは少なそうなため! | 件数が増えて一覧の描画が重くなったとき・ほぼ完成して時間に余裕がある時 | ### 除外(作らないと決めた) @@ -130,9 +130,9 @@ すべて満たした時点で MVP 完成とする。 -- [ ] 画面が `docs/ui-mockups/` の **9 枚と一致している** +- [ ] 画面が `docs/ui-mockups/` の **10 枚と一致している** - [ ] **本番環境(Vercel)** で父の Google アカウントからログインでき、登録〜編集〜削除まで通る - [ ] 他人のデータが**見えない・操作できない**(別アカウントで確認) -- [ ] 同じ物を 2 回登録しようとすると**弾かれ、既存のボトルへ辿れる** +- [ ] 同じ物を**登録・編集のどちらでも作れず**、既存のボトルへ辿れる - [ ] 実データ規模(200〜300 件)で一覧・傾向ページが壊れない - [ ] モバイル幅で全画面が崩れない diff --git "a/docs/ui-mockups/05\343\203\27306-\351\207\215\350\244\207\346\244\234\345\207\272.png" "b/docs/ui-mockups/05\343\203\27306-\351\207\215\350\244\207\346\244\234\345\207\272.png" new file mode 100644 index 0000000..9733ddc Binary files /dev/null and "b/docs/ui-mockups/05\343\203\27306-\351\207\215\350\244\207\346\244\234\345\207\272.png" differ