# T516 — fe-radar-watches-page

> Status: `done`  
> Milestone: `M1`  
> Kind: `feat`  
> Est. change: `~200 lines`

## Goal

完成後系統應：`/app/radar/watches` 可建立／編修／暫停／恢復／封存雷達訂閱，並可採用 AI 關鍵字建議。

## Depends on

- T502、T503、T513、T514

## Inputs

- Spec：`../spec.md` §4.2、§5.3、§9.1
- 既有頁面骨架：`apps/web/src/pages/ScoutPage.tsx`、路由設定 `src/App.tsx`

## Outputs

### 程式變更（預期路徑）

| 路徑 | 動作 | 說明 |
|------|------|------|
| `apps/web/src/pages/RadarWatchesPage.tsx` | add | 列表＋新增／編輯抽屜＋狀態動作 |
| `apps/web/src/components/radar/WatchSuggestPanel.tsx` | add | 建議清單，逐條採用／全採用 |
| `apps/web/src/App.tsx` | edit | 註冊 `/app/radar/watches` 路由 |
| `apps/web/src/data/live/radarRepos.ts` | edit | watches CRUD／pause／resume／suggest 實作 |
| `apps/web/src/styles/radar.css` | edit | 列表與抽屜樣式 |

### 行為變更

- 未建服務檔案時，頁面顯示引導並停用「新增」，附連結到獨立 `/app/policy`。
- 觸到 `max_active_watches` 時直接顯示後端 message（含上限與升級提示），不用前端自行推測文案。
- 每列顯示 `terms`、地區、狀態、`last_swept_at`。

## Out of scope

- 手動觸發即時巡的 UI（T533 提供 API，UI 併入 T552 或此頁後續）
- 今日商機頁（T551）

## Acceptance

- [x] 無服務檔案時「新增」被停用並有引導
- [x] 建立→暫停→恢復→封存的狀態變化正確反映
- [x] 指令：

```bash
cd apps/web && npm run build && npm run check:tokens && npm run test && npm run lint
```

## Notes

### 實作落點

- `apps/web/src/pages/RadarWatchesPage.tsx`：清單（關鍵字／排除詞 chip、狀態 badge、地區、`last_swept_at`）＋狀態篩選＋分頁＋新增／編輯表單。
- `apps/web/src/components/radar/WatchSuggestPanel.tsx`：按下才問 AI（避免開頁就燒 credit），逐條／全部採用，已在表單裡的詞顯示「已採用」並停用按鈕。
- `apps/web/src/App.tsx`：`/app/radar/watches` lazy 路由。側欄項在 T553。
- `apps/web/src/styles/radar.css`：`.hb-radar-form`、`.hb-radar-suggest*`。

### 決策

- **採用建議不等於建立訂閱**：建議只往表單塞字，仍要按儲存才會打 API。使用者可以先刪掉不要的再送。
- **關鍵字用換行／逗號／頓號都能分隔**：使用者常直接從別處貼一整串過來。
- **每個動作後重讀清單**：暫停／恢復會連動 `active_count` 與配額提示，局部改 state 很容易和後端對不起來。
- **配額與缺服務檔案的文案來自後端**：`403003`（含「1 of 1 on your plan」與升級提示）與 `400100` 都直接顯示後端 message，前端不自行推測上限。
- **建立失敗時表單不關**：使用者可以改成「不立刻啟用」或先去暫停別的再送。
- 封存列只顯示「刪除封存設定」；刪除前明確確認，刪除後設定從列表消失，但歷史商機、巡邏紀錄與統計保留。封存仍不可恢復或編輯。

### 測試

`src/pages/RadarWatchesPage.test.tsx`（5 案）：無服務檔案停用新增＋指路、建立→暫停→恢復→封存的狀態與配額連動、配額滿顯示後端原句且表單留著、建議逐條採用分流到關鍵字／排除詞、建議失敗說原因。
