# spec.md — D-Watch 機能仕様とマイルストーン

本書は「**何を作るか（機能一覧）**」と「**どの順で作るか（開発フェーズ）**」の正です。
仕様の詳細は `CLAUDE.md`（確定仕様）と `d-watch-api.yaml`（API契約）を参照してください。

---

## 1. 画面一覧（全10画面）

| # | 画面 | ID | 種別 | 概要 |
|---|---|---|---|---|
| 1 | ダッシュボード | `dashboard` | 参照 | ログイン後の最初の画面。昨夜の清掃結果を10分で把握（★数値カードと表は各清掃エリアの直近の清掃・7日以内。D-159★） |
| 2 | 清掃詳細ログ | `detailLog` | 参照 | 1行=1ロボット×1スケジュール実行。△×行を展開してアドバイス表示 |
| 3 | アラート（事前チェック） | `alert` | 参照+更新 | 清掃前の異常検知。対応状況を更新できる |
| 4 | レポート | `report` | 参照 | KPI4指標＋前期比、施設別パフォーマンス、連続問題施設、エラー種別内訳 |
| 5 | 点検記録 | `inspection` | 参照+登録 | 現場訪問時のBefore/After写真と申し送り。**独自ストア** |
| 6 | D-Secログ | `dsecurity` | 参照 | D-S制御盤の稼働状況一覧 |
| 7 | D-Secアラート | `dsalert` | 参照+更新 | 制御盤の通信断（`D-S_CONTROL_FAILURE`）の検知。対応状況・備考を更新できる（編集モード。D-180）（種別「セキュリティ無効」はコードに残る — U6 — が DB からは出ない） |
| 8 | 施設ロボットマスタ | `robot` | 参照のみ | 施設情報＋ロボット情報。登録更新はD-Security側画面 |
| 9 | スケジュール | `schedule` | 参照のみ | Pudu Cloud由来の清掃スケジュール |
| 10 | システム管理 | `system` | 設定 | 表示設定・運用設定（閾値等）。**P1は限定スコープ**（下記2.3） |

---

## 2. 機能仕様

### 2.1 モニタリング系

#### F-01 ダッシュボード
- 数値カード: 成功 / 失敗 / 未実施 / ★結果待ち★ / 成功率（★結果待ちは D-173 で追加。成功率の分母に入れない★）
  - ★母集団は「清掃エリアごとに、予定が終了した本清掃のうち一番新しい1件（7日以内・出発失敗と結果待ちを含む）」★（D-159 / D-173。旧: 当日の日付の清掃。★D-173 以前は「終了＋30分（`settleMinutes`）」を待っていた★）。`?date=` を指定したときはその日の清掃
  - ★結果待ち★ ＝ 本清掃のレポートが無く、予定の開始 ≤ 今 ＜ 予定の終了＋`RESULT_WAIT_MINUTES`（120分。設定値）。★失敗・未実施に数えない★（D-173）
  - ★スケジュール進捗・要レスキュー・セキュリティバナーは当日のまま★
- D-Secアラートバナー（セキュリティ無効施設の赤警告）（★DB のデータでは出ない — D-Sec アラートに `security_off` を作らないため。コードは残す。D-180 項7・U7★）
- 清掃詳細ログ（コンパクト版。機種・シリアル列を省略）
- スケジュール進捗（完了数/予定数＋プログレスバー）
- 失敗の内訳（未実施 / セキュリティ有効 / D-S制御盤異常 / ロボット異常）
- 各ブロックは表示設定でON/OFF可能
- ★削除済み（`deleted_at` が NULL でない）の施設・清掃エリアの行は、数値カード・コンパクト版ログ・予定件数・要レスキューのどれにも出さない（D-182）★
- API: `GET /dashboard` の1本（★コンパクト版ログはレスポンスの `problem_logs`。数値カードと同じ母集団の △×（★結果待ちの行は出さない。D-173★）。画面は先頭5件★。D-159 / 指示書AV D7。旧: `GET /clean-logs?result=problem` を再利用）

#### F-02 清掃詳細ログ
- カラム: 施設名 / 機種 / シリアル / 日付 / 予定時刻 / 事前チェック1回目 / 事前チェック2回目 / 出発 / 完了率 / 判定
  - ★事前チェックの2列は分数ではなく順番で割り当てる（早い方が1回目。何分前かはセルの中に出す）★（D-178。旧見出し 1h前ST / 30分前ST）。API は `status_1h` / `status_30m`（名前は互換のため据え置き）と `status_1h_minutes` / `status_30m_minutes`
  - ★事前チェック1回目 / 2回目は、その回の事前チェックの予定が無いときは「-」★（API は `null`、CSV は空のセル。予定があって異常なしは "Standby."）。★判定には使わない★（D-171 / D-170）
  - ★結果待ちの行（`result_pending`。予定の終了から `RESULT_WAIT_MINUTES` 以内でレポートが無い）は、出発「…」・清掃結果「結果待ち」・判定なしで、行を開けない★。★△×のみ／×のみの絞り込みからは外す★（D-173）
- フィルタ: 判定（△×のみ=デフォルト / 全件 / ×のみ）、グループ、会社、日付範囲（★**最大731日＝2年**★。★2026-09-15 に31日から拡大。D-136★。★既定は期間を指定しない（全期間）。D-159 追記★）
- 検索: 施設名・機種・シリアル
- ★削除済み（`deleted_at` が NULL でない）の施設・清掃エリアの行は出さない（件数・CSV・行を開いた先も。見えない行の `{id}` は404。施設名で検索すると0件。D-182）★
- ★清掃エリアに有効なロボットがいない行は、機種・シリアルが「-」（API は `null`、CSV は空のセル。D-182）★
- CSVエクスポート（★日付は年つき（`YYYY/MM/DD`。D-182）★。★値が無い率は空のセル（D-164）。事前チェックは「事前チェック1回目 / 1回目（分前） / 事前チェック2回目 / 2回目（分前）」の4列で、予定が無い回は空（D-171 / D-178）。結果待ちの行は出発・清掃結果が「結果待ち」、完了率・判定は空（D-173）★）
- **行展開アドバイス（Phase A）**: △×行クリックで R1〜R4 の推定原因とアクションを表示
- エリア別完了率グラフ: ★結果待ちのエリア（`result_pending`）は問題エリアに数えない（D-182）★。**同じ施設・同じ日のエリア（＝清掃ロボット）一覧を出す**（TeamWorks 第8回 A9-2 で「1エリア=1ロボットにつき1つの完了率」が取得可能と確定。データ元は `clean_status_histories`）。★1回の清掃の中の区域別内訳（確認事項#10 の本来の問い）は依然として未解決★
- データ量: ★**約30〜45行/日、年間約1.1〜1.6万行**★、保持2年（★★**いずれも実測ではなく
  見積もりです**★★）
  - ★根拠★: ★ロボット30台未満（DFA把握。2026-09-17 ユーザー申告）★ ×
    ★1台1日あたり1〜1.5回（★**仮置き**★）★
  - ★★**注意: 1台1日あたりの回数は実測値ではありません**★★ — ★2026-09-17 の旧記載は
    テストDB（`133.125.238.144`）の `clean_timetables WHERE precheck=0` を数えたものだったが、
    ★**その行は当方（DFA）が `scripts/seed-local-db.ts` で投入したダミーデータ**★だった。
    ★本番の清掃回数は**不明**★。出典: `docs/handoff/テストDBのデータ出所_20260917.md`
  - ★**旧記載「約360行/日、年間約13万行」は施設200件を前提にした外挿**★で、実態と大きく違っていた。
    ★この数字を根拠にしたコメントが `lib/` と `app/` に14か所残っている★（★未修正★）。
    ★書き換えるときは、新しい数字も「見積もり」であることを明記すること★。
    出典: `docs/handoff/規模前提の見直し_20260917.md`
- API: `GET /clean-logs`, `/clean-logs/{id}/advice`, `/clean-logs/{id}/areas`, `/clean-logs/export`

#### F-03 アラート（事前チェック）
- サマリーカウント（未解消 / 解消 / 合計）
- 種別: 1h前異常 / 30分前異常 / 出発失敗（★結果待ちの清掃は出発失敗に出さない。D-173★）
- ★本清掃が成功した（親の本清掃が ◎○）事前チェック異常のカードは、薄く表示して「本清掃は成功」の Pill を出す★（`main_run_succeeded`。★対応状況・未解消の件数は変えない★。D-171）
- 対応状況: 未解消 / 対応中 / 解消を ★プルダウンで選ぶ★（`responded_at` / `resolved_at` を記録）。★D-161。旧: 押すたびに 未解消 → 対応中 → 解消 と巡回するボタン★
- ★対応状況と備考は「編集モード」でだけ変えられ、「保存」で変えた行をまとめて送る★（即時保存はしない。D-165）
- フィルタ: 日付範囲、グループ、会社、対応状況（★開始日が終了日より後なら422。D-182★）
- ★削除済み（`deleted_at` が NULL でない）の施設・清掃エリアの行は出さない（件数にも数えない。その行への `PATCH /prechecks/{id}/status` は404。D-182）★。★`robot_error_reports.status` が 0/1/2 以外の行も出さない（PATCH は404。D-182）★
- API: `GET /prechecks`, `PATCH /prechecks/{id}/status`

#### F-03 補足 — エラー一覧（`/errors`）★2026-10-06 追加。番号は振らない★
- ★`robot_error_reports` の全行★（ロボット・制御盤・セキュリティ・清掃開始失敗。事前チェックかどうかを問わない）を一覧する。★削除済み（`deleted_at` が NULL でない）の施設・清掃エリアの行は出さず（件数にも数えない）、その行への `PATCH /errors/{error_id}/status` は404（U8。D-180 項11）★。既定は未解消・対応中、「解消済みも表示」で解消も（D-160）
- カラム: 検知時刻 / 施設名 / シリアル / 種類 / レベル / 紐づく予定 / 対応状況 / 備考。★種類・レベル・シリアルは DB の値のまま★（区分を作らない。D-077 / D-160 項3）。★列は足さない★。種類の2行目にエラー内容（未選択 / 詳細 / その他は大分類名 / 選べない行は `-`）。編集モードではボタンから窓（キーワード・分類・件数・クリア）で `error_list_id` を選ぶ。その他（`note_required`）は備考必須。分類名・詳細は `t()` に通さない（D-185 / 指示書BE）
- 対応状況と備考は F-03 と同じ規則（プルダウン・編集モードと保存。`responded_at` / `resolved_at` を記録。D-161 / D-165）。★エラー一覧の PATCH だけ `error_list_id` も書く★（アラートと D-Sec は4カラムのまま。D-184）
- フィルタ: 日付範囲（検知時刻）、グループ、会社、施設名、種類（部分一致）
- ★モックに無い画面★。F-03 のアラート画面は事前チェック＋出発失敗のまま
- API: `GET /errors`, `PATCH /errors/{error_id}/status`

#### F-04 レポート
- 週次/月次トグル（KPIカード・列見出しが「前週比」「前月比」に連動）
- KPI4列＋前期比: 稼働率 / 出発成功率 / 清掃完了率 / エラー発生率
- 連続問題施設（2日以上連続、上位10件＋もっと表示）
- エラー種別内訳（7分類、件数降順）
- 施設別パフォーマンス（稼働率ワースト順、上位10件＋もっと表示、良好/注意/要改善フィルタ）
- ★施設別パフォーマンスの CSV エクスポート★（表と同じ条件・同じ並びで、件数の上限なしに全施設。値が無い率は空欄。D-163 / D-164）
- **データソース切替**: 確定期間=サマリテーブル / 当期=詳細テーブルからその場集計（API内部で吸収しフロントには見せない）
- ★削除済みの施設・清掃エリアの行は出さない（当期の集計・連続日数にも入れない。D-182）★。★確定期間はサマリ（TeamWorks の集計値）なので外せる範囲だけ — `customer_summaries`（KPI）は何も外せず、`facility_summaries`・`facility_error_summaries` は施設の削除だけ外す（清掃エリアの削除は施設の値に含まれうる。D-182）★
- ★その月に無い週（例 2026年9月の第6週）は422。KPI の顧客とグループは AND（傘下でなければ 200・空）。D-182★
- API: `GET /reports/kpi`, `/reports/facility-performance`, `/reports/facility-performance/export`, `/reports/continuous-problems`, `/reports/error-breakdown`

#### F-05 点検記録 ★独自ストア★
- 一覧＋サマリ（総点検数 / 点検施設数 / 申し送りあり）
- 詳細: Before/After × 7項目の写真グリッド（ブラシ / ダストボックス / フィルター / スクイジー / 吸引ホース / 汚水タンク / ブラシ裏面）＋申し送り事項
- 新規登録: 訪問日・施設・記入者・所属会社・ロボット＋写真アップロード（最大14枚、1枚10MBまで）
- 削除（論理削除）
- **メインDBとは別のDFA独自ストア（SQLite + オブジェクトストレージ）に保存する**
- API: `GET /inspections`, `GET/DELETE /inspections/{id}`, `POST /inspections`

### 2.2 D-Security系

#### F-06 D-Secログ
- サマリカード（合計 / 正常 / 異常）
- 一覧: シリアル / 施設名 / 接続状態 / 最終通信 / 備考
- 備考は編集モード（編集 → 保存（N件）/ キャンセル）で変えた行をまとめて保存（★即時保存はしない★。保存先はファイル `data/settings/ds-device-notes.json`。D-183）
- **自動ソート: 異常 → 通信断 → 正常**（サーバ側）
- ★まだ一度も通信が無い制御盤は最終通信「-」（API は `null`）。`controls.status` が 0/1 以外の制御盤は出さない（備考の保存も404）。D-182★
- ステータスフィルタ
- ※トークン期限/残日数は**仕様から除外済み**（実装しない）
- API: `GET /ds-devices`

#### F-07 D-Secアラート
- サマリーカウント（未解消 / 対応中 / 解消済み。★アラート画面と同じラベル。D-181★）
- 状態の絞り込み: 未解消のみ（★対応中も含む★）/ 全件 / 解消済み（★アラート画面と同じ文言。D-181★）
- 種別: セキュリティ無効 / 通信断（★DB からは通信断だけ。セレクトは残す — U6★）
- 対応状況の遷移: 未解消 → 対応中 → 解消（編集モードでまとめて保存。D-161 / D-165）
- API: `GET /ds-alerts`, `PATCH /errors/{error_id}/status`（エラー一覧と同じ書き込み。編集モード。D-180）

### 2.3 マスタ・設定系

#### F-08 施設ロボットマスタ（参照のみ）
- 施設情報（名称 / 都道府県 / 住所 / 電話 / 責任者 / 担当者 / メール）
- 契約フラグ表示（D-Watch ○/ー、D-Sec ○/ー）← `facilities.contract` が正
- ロボット情報（メーカー / 機種 / シリアル）
- 検索: 施設名 / シリアル / 住所
- **登録・更新はD-Security側の画面で行うため、D-WatchはGETのみ**
- API: `GET /facilities`

#### F-09 スケジュール（参照のみ）
- シリアル / 施設名 / 曜日 / 開始 / 終了 / 有効
- Pudu Cloud由来。変更はPudu Cloud管理画面で行う
- ★削除済みの施設・清掃エリアのスケジュールは出さない。清掃エリアに有効なロボットがいない行のシリアルは「-」（API は `null`）。D-182★
- API: `GET /schedules`

#### F-10 システム管理（P1限定スコープ）
| サブタブ | P1での扱い |
|---|---|
| 表示設定 | **実装する**（ダッシュボードの表示項目ON/OFF）。設定ファイル管理 |
| 運用設定 | **実装する**（事前チェックのタイミング、完了率閾値、エスカレーション閾値） |
| 通知設定 / 顧客管理 / グループ管理 | **実装しない**（D-Security側の画面を流用） |
| ユーザー管理 / 権限管理 / 監査ログ | **スコープ外**（ユーザー管理未実装のため。P2以降） |

---

## 3. 開発フェーズとマイルストーン

```
P0 環境準備 ──> P1 基盤 ──┬──> P2 コア画面 ──> P3 アラート系 ──> P4 レポート ──> P5 マスタ ──> P7 仕上げ
                          └──> P6 点検記録（独立・並走可）
```

### M0 — 環境準備 `P0`
**ゴール: 2人がコードを書き始められる状態**

- [ ] GitHubプライベートリポジトリ作成、Next.js + TypeScript + Prisma 初期化
- [ ] `CLAUDE.md` / `.cursorrules` / `docs/` 配置
- [ ] **`lib/fixtures/` のサンプルデータ作成**（`docs/fixtures.md` のケースを網羅）
  - モックJSXの `LOG` / `PRECHK` / `DSA_INIT` / `RANK` / `ROBOTS` / `SCHED` / `DSDEV` / `INSPECTIONS` を型付きで移植
  - **実在の店舗名・住所・電話番号をダミー値に置換**（バリューマート等）
  - 判定の境界値10ケース、L1〜L3、契約フラグ4パターン、空配列、11件以上を追加
- [ ] `lib/db/index.ts` に `USE_FIXTURES` の切替を実装
- [ ] CI（Lint + テスト自動実行）
- [ ] Cursorの Privacy Mode を両名ON（会社コードのため必須）

**依存**: なし（今すぐ着手可） **担当**: 環境

---

### M1 — 基盤 `P1`
**ゴール: 全画面が乗る土台と、絶対に間違えられない判定ロジックの完成**

- [ ] 共通レイアウト（サイドバー10画面ナビ、ダーク/ライト、JP/EN切替の器。★選んだ値はブラウザに保存し、再読み込み・再ログインのあとも保つ（D-183）★）
- [x] API共通実装（★認証（`proxy.ts` + cookie + IP制限。指示書I / D-101）★、エラー形式、ページネーション）
- [ ] **判定ロジック + 期待値表テスト** ★最重要・先行実装★
  - ◎○△×判定（境界値: 90.0 / 90.1 / 70.0 / 10.0 / 未実施）
  - L1〜L3エスカレーション（スケジュール存在日の連続カウント）
  - KPI4指標の計算式
  - 整数丸め（判定は生値、表示は丸め値）
- [ ] Prisma接続とスキーマ取り込み（`db pull`）、テーブル定義書との突き合わせ
  - **取り込みと突き合わせだけ早めに実施する**（カラム名の食い違いが後から見つかると全画面に波及するため）
  - 画面開発は引き続き fixture で進める

**依存**: スキーマ取り込みのみステージングDB待ち。**判定ロジック・画面開発はfixtureで先行可**
**担当**: 両名（判定ロジックは特に慎重に）

---

### M2 — コア画面 `P2`
**ゴール: 毎朝の運用（STEP1〜2）が回る**

- [ ] ダッシュボード（F-01）
- [ ] 清掃詳細ログ（F-02）: 一覧・フィルタ・検索・CSV
- [ ] 行展開アドバイス（R1〜R4）
- [ ] エリア別完了率（施設×日付のエリア一覧。TeamWorks 第8回 A9-2 で取得可能と確定）

**依存**: なし（fixtureで開発） **担当**: ダッシュボード

---

### M3 — アラート系 `P3`
**ゴール: 検知から対応記録までが完結する**

- [ ] アラート（F-03）: 一覧 + 対応状況PATCH
- [ ] D-Secログ（F-06）: 自動ソート込み
- [ ] D-Secアラート（F-07）: 一覧 + 対応状況PATCH（2026-10-19 実装。PATCH は /errors/{error_id}/status。D-180）

**依存**: 更新系はDB更新用アカウントと `responded_at`/`resolved_at` カラム反映待ち
**担当**: ダッシュボード

---

### M4 — レポート `P4`
**ゴール: 週次・月次の振り返りとInsight Assistレポートの素材が出せる**

- [ ] KPI4指標＋前期比（F-04）
- [ ] 施設別パフォーマンス / 連続問題施設 / エラー種別内訳
- [ ] サマリテーブル参照と当期その場集計の切替

**依存**: サマリ集計バッチ（TeamWorks）の完成とスキーマ確定待ち。**当期集計部分は先行可**
**担当**: 両名

---

### M5 — マスタViewer `P5`
**ゴール: 契約状態とスケジュールが画面から確認できる**

- [ ] 施設ロボットマスタ（F-08、GET専用）
- [ ] スケジュール（F-09、GET専用）
- [ ] システム管理の表示設定・運用設定（F-10）

**依存**: `facilities.contract` 反映済みDB待ち。実装は単純なので後回し可
**担当**: ダッシュボード

---

### M6 — 点検記録 `P6` ※他フェーズと並走可
**ゴール: 現場訪問記録がシステムに残る**

- [ ] 独自データストア構築（SQLite + オブジェクトストレージ）
- [ ] 一覧・詳細（Before/After×7項目）・新規登録（写真アップロード）・削除
- [ ] 過去のGフォームデータのCSVインポート（任意）

**依存**: **メインDBと独立のため、いつでも着手可能** **担当**: 環境

---

### M7 — 仕上げ `P7`
**ゴール: 本番稼働**

- [ ] さくらクラウドVMへのデプロイ、IP制限設定
- [ ] **`USE_FIXTURES=false` に切替**、1画面ずつ実データで確認
- [ ] **実データでの数値検証**（サマリテーブルの値と画面表示の一致確認、判定結果がfixture時と同ロジックで出ているか）
- [ ] fixtures は削除せず残す（テスト・デモ・障害切り分け用）
- [ ] JP/EN翻訳の適用
- [ ] 運用ドキュメント（デプロイ手順・障害時対応）

**依存**: 本番DB・VM設置（TeamWorks）待ち **担当**: 環境

---

## 4. TeamWorks待ち項目の一覧

実装をブロックしないため、以下は**スタブ/モックで先行**し、解消後に差し替えます。

| 項目 | 影響機能 | 暫定対応 |
|---|---|---|
| DBアカウント発行（参照用/更新用） | 全画面 | **`lib/fixtures/` のサンプルデータで開発** |
| ステージングDB | M1のスキーマ取込以降 | Prismaスキーマを定義書ベースで手書きし、`db pull` で答え合わせ |
| `facility_id` の主要テーブル追加（依頼3-1） | 全画面のクエリ | 多段JOINで暫定実装 → 追加後に単純化 |
| `responded_at` / `resolved_at` の2カラム化（依頼3-2） | F-03, F-07 | APIスキーマは2カラム前提で先行 |
| サマリ集計バッチ | F-04 | fixtureのサマリ値で開発。当期集計ロジックで全期間を暫定計算 |
| ~~月跨ぎ週の帰属ルール回答（依頼3-4）~~ **回答受領・確定済み** | F-04 | ★**週を月境界で分割する**（D-071。TeamWorks 方式）。週の長さは1〜7日、週番号は1〜6★ |
| ~~Pudu Cloud エリア別完了率（確認事項#10）~~ **A9-2 受領・エリア単位は確定** | F-02のエリア別グラフ | ★エリア（＝ロボット）単位の完了率は `clean_status_histories` から本実装する★。★1回の清掃の中の区域別内訳は依然として未解決（第9回で聞き直し）★ |

### 開発とDB接続の関係

```
M0 ──────────────────────────────────────────> M7
   fixtures で全画面を開発 ────────────> USE_FIXTURES=false
                    ↑
        M1でdb pullによるスキーマ突き合わせだけ先に済ませる
        （画面開発はfixtureのまま継続）
```

**DB接続は「ある程度画面ができてから」。** ただしスキーマの答え合わせ（`db pull`）だけはM1で早めに行い、
カラム名の食い違いを早期に潰します。

---

## 5. Phase B / C（P1スコープ外・将来）

| 機能 | 内容 |
|---|---|
| Phase B | エラーコード別アドバイスルール（ハンドルアップ/ブラシ/LostLocation/オフライン等）、★1回の清掃の中の区域別完了率（売り場・通路・レジ前などの内訳。確認事項#10 の本来の問い。エリア＝ロボット単位はP1で実装済み）★ |
| Phase C | 自動検出・予兆検知 |
| P2 | 顧客アカウント開放（顧客管理者・施設運用者）、ユーザー管理・権限管理・監査ログ、Keenon対応 |
