Feature #1365
是由 陳國瑋 於 28 天 前更新
## 背景
Branch Admin UI 後續會串接 Connector Status websocket。這張票定義 FE 收到 websocket connector status 後的顯示與 CTA 規則,避免未知或未處理狀態被誤判成可開始充電。
目前程式碼調查結果:
- Branch Admin 現況尚未透過 websocket 取得 connector status,connector detail 目前主要走 `GET /api/connector/status/{connectorId}` polling。
- `ems_branch` 的 websocket endpoint 目前是 OCPP charge point 使用的 `/ws/ocpp/*`,不是 browser admin UI 使用的 connector status websocket。
- browser 使用者端既有 connector update websocket 由 `ems_hq` 發送,payload DTO 為 `ConnectorUpdateMessage`,狀態位於 `connectorData.status`。
- `ems_branch` 發 MQ connector event 時,status 來源是 `ConnectorStatusMapper.mapToDisplayStatus(connector)`。
- 另開 API ticket 要求 websocket response 在 `connectorData` 增加 `statusLabel`,用來表示 status 的中文顯示文字。
## WebSocket response 欄位需求
FE 預期 websocket message 外層格式:
```json
{
"eventType": "UPDATED",
"buildingId": "building-id",
"connectorId": "connector-id",
"timestamp": "2026-07-06T18:30:00",
"connectorData": {
"id": "connector-id",
"buildingId": "building-id",
"areaType": "public",
"status": "CHARGING",
"statusLabel": "充電中",
"connectorType": "J1772",
"image": null,
"startTime": "2026-07-06T18:00:00",
"totalEnergy": 12.34,
"energyUsage": 3.21,
"remainTime": 45,
"currentVoltage": 220.0,
"currentCurrent": 16.0,
"currentPower": 3.52,
"costPredict": 25.0,
"currentTariff": 7.5,
"enableOffPeak": true,
"nextOffPeakTimeStart": "2026-07-06T22:00:00",
"nextOffPeakTimeEnd": "2026-07-07T06:00:00"
}
}
```
`statusLabel` 說明:
- 欄位位置:`connectorData.statusLabel`
- 型別:`string | null`
- 目的:後端提供 status 對應的中文顯示文字,讓不同 client 對中文狀態命名一致。
- FE fallback:若 `statusLabel` 為空或尚未由 API 提供,FE 仍必須依本票 UI mapping table 的 label 顯示,不可 fallback 成 `AVAILABLE`。
## 後端可能回傳的 status
後端 enum 來源:`java/ems_branch/src/main/java/com/sylksoft/ems/branch/model/enums/ConnectorStatus.java`
注意:實際 event payload 使用 enum 的 `code`,所以 suspended 狀態會是 `SUSPENDEDEVSE` / `SUSPENDEDEV`,不是 enum name 的 `SUSPENDED_EVSE` / `SUSPENDED_EV`。
## FE 正規化規則
1. 對 raw status 做 `trim()` 與 `toUpperCase()`。
2. Alias mapping:
- `SUSPENDED_EVSE` 必須視為 `SUSPENDEDEVSE`
- `SUSPENDED_EV` 必須視為 `SUSPENDEDEV`
3. `null`、空字串、未列入表格的新 status,不可以 fallback 成 `AVAILABLE`。
4. Unknown status 應顯示 `statusLabel || '未知狀態'`,不顯示開始/停止 `未知狀態`,不顯示開始/停止 CTA,並保留 raw status 供 debug。
5. 正常狀態的畫面文案優先使用 `connectorData.statusLabel`;若沒有此欄位或值為空,才使用 UI mapping table 的中文 label fallback。
Alias 原因:後端真實 payload 目前來自 `ConnectorStatus.code`,但 OpenAPI/TypeScript schema 或 enum name 可能出現 `SUSPENDED_EVSE` / `SUSPENDED_EV`。FE 同時支援兩種寫法,可以避免序列化或 schema 差異造成 UI fallback 錯誤。
## UI mapping table
| Raw status | Accepted alias | Fallback UI label | Expected `statusLabel` | 語意 | Badge class | 色碼參考 | Indicator | Animation | Primary CTA | CTA enabled | 備註 |
|---|---|---|---|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|---|---|---|
| `AVAILABLE` | - | 待命 | 待命 | 可開始使用 | `bg-blue-100 text-blue-700 border-blue-200` | bg `#DBEAFE`, text `#1D4ED8`, border `#BFDBFE` | `bg-blue-500` (`#3B82F6`) | none | 開始充電 | yes | 只有這個 status 可以顯示開始充電。 |
| `ENQUEUED` | - | 排隊中 | 排隊中 | 已進入排隊/輪充等待 | `bg-cyan-100 text-cyan-700 border-cyan-200` 或 semantic `bg-info/10 text-info border-info/20` | cyan indicator `#06B6D4` | `bg-cyan-500` (`#06B6D4`) | none | 取消排隊 / read-only | conditional | 若 branch admin 有取消排隊 API/權限,顯示取消排隊;否則僅 read-only,不可顯示開始充電。 |
| `PREPARING` | - | 準備中 | 準備中 | 插槍/交易準備中 | `bg-orange-100 text-orange-700 border-orange-200` | bg `#FFEDD5`, text `#C2410C`, border `#FED7AA` | `bg-orange-500` (`#F97316`) | pulse | 停止充電 | yes | 充電流程已啟動,不可顯示開始充電。 |
| `CHARGING` | - | 充電中 | 充電中 | 充電進行中 | `bg-emerald-100 text-emerald-700 border-emerald-200` | bg `#D1FAE5`, text `#047857`, border `#A7F3D0` | `bg-emerald-500` (`#10B981`) | pulse | 停止充電 | yes | 顯示即時充電資訊。 |
| `SUSPENDEDEVSE` | `SUSPENDED_EVSE` | 充電站暫停 | 充電站暫停 | 充電站端暫停供電 | `bg-orange-100 text-orange-700 border-orange-200` | bg `#FFEDD5`, text `#C2410C`, border `#FED7AA` | `bg-orange-500` (`#F97316`) | pulse | 停止充電 | yes | 仍屬於既有交易/插槍流程,不可顯示開始充電;用 warning,不用 fault red。 |
| `SUSPENDEDEV` | `SUSPENDED_EV` | 車輛暫停 | 車輛暫停 | 車輛端暫停取電 | `bg-orange-100 text-orange-700 border-orange-200` | bg `#FFEDD5`, text `#C2410C`, border `#FED7AA` | `bg-orange-500` (`#F97316`) | pulse | 停止充電 | yes | 仍屬於既有交易/插槍流程,不可顯示開始充電;用 warning,不用 fault red。 |
| `FINISHING` | - | 結束中 | 結束中 | 交易正在結束 | `bg-yellow-100 text-yellow-700 border-yellow-200` | bg `#FEF9C3`, text `#A16207`, border `#FEF08A` | `bg-yellow-500` (`#EAB308`) | none | 不顯示開始;可 disabled 顯示「結束中」 | no | OCPP `FINISHING` 不是完成可用;真正可再開始是 `AVAILABLE`。 |
| `RESERVED` | - | 已預約 | 已預約 | 已被預約/佔用 | `bg-gray-100 text-gray-700 border-gray-200` | bg `#F3F4F6`, text `#374151`, border `#E5E7EB` | `bg-gray-400` (`#9CA3AF`) | none | 不顯示開始;可 disabled 顯示「已預約」 | no | 中性佔用狀態,不是故障,也不是可操作狀態。 |
| `UNAVAILABLE` | - | 不可用 | 不可用 | 站點不可用/停用 | `bg-gray-100 text-gray-700 border-gray-200` | bg `#F3F4F6`, text `#374151`, border `#E5E7EB` | `bg-gray-400` (`#9CA3AF`) | none | 不顯示開始;可 disabled 顯示「不可用」 | no | 不可誤判為待命。 |
| `FAULTED` | - | 故障 | 故障 | 故障 | `bg-red-100 text-red-700 border-red-200` | bg `#FEE2E2`, text `#B91C1C`, border `#FECACA` | `bg-red-500` (`#EF4444`) | pulse | 不顯示開始;可 disabled 顯示「故障」 | no | 若 payload 有 error/vendor code,應顯示或保留於 detail/debug。 |
| unknown / empty / null | - | 未知狀態 | null 或後端原樣提供 | 未支援狀態 | `bg-gray-100 text-gray-700 border-gray-200` | bg `#F3F4F6`, text `#374151`, border `#E5E7EB` | `bg-gray-400` (`#9CA3AF`) | none | 不顯示開始/停止 | no | 必須保留 raw status;不可 fallback 成 `AVAILABLE`。 |
## Acceptance Criteria
- Branch Admin connector status UI 有一份集中式 status mapping,websocket 與現有 HTTP polling status 顯示共用同一套規則。
- FE 讀取 websocket payload 的 `connectorData.status` 作為狀態邏輯判斷依據。
- FE 顯示中文狀態時優先使用 `connectorData.statusLabel`;若該欄位不存在、為空或為 null,才使用本票 mapping table 的 fallback label。
- FE 正確處理表格內所有 raw status:`AVAILABLE`、`PREPARING`、`ENQUEUED`、`CHARGING`、`SUSPENDEDEVSE`、`SUSPENDEDEV`、`FINISHING`、`RESERVED`、`UNAVAILABLE`、`FAULTED`。
- FE 同時接受 alias:`SUSPENDED_EVSE`、`SUSPENDED_EV`。
- `AVAILABLE` 是唯一可顯示「開始充電」CTA 的狀態。
- `PREPARING`、`CHARGING`、`SUSPENDEDEVSE`、`SUSPENDEDEV` 可以顯示「停止充電」CTA。
- `ENQUEUED` 只有在 branch admin 已有取消排隊 API/權限時才顯示「取消排隊」;否則 read-only。
- `FINISHING` 顯示為「結束中」,不可顯示開始/重新開始 CTA。
- `RESERVED`、`UNAVAILABLE`、`FAULTED`、unknown/null/empty 都不可顯示開始 CTA。
- unknown/null/empty 不可 fallback 成 `AVAILABLE`,並應保留 raw status 方便 debug。
- 請補上每個 status 的 mock fixture、unit test 或 story/manual QA case;若目前專案沒有對應測試框架,至少要有可手動驗證的 fixture/demo 狀態清單。
## Code references
- `java/ems_branch/src/main/java/com/sylksoft/ems/branch/model/enums/ConnectorStatus.java`
- `java/ems_branch/src/main/java/com/sylksoft/ems/branch/util/ConnectorStatusMapper.java`
- `java/ems_branch/src/main/java/com/sylksoft/ems/branch/mq/publisher/ConnectorEventPublisher.java`
- `java/ems_hq/src/main/java/com/sylksoft/ems/hq/websocket/dto/ConnectorUpdateMessage.java`
- `java/ems_hq/src/main/java/com/sylksoft/ems/hq/mq/model/ConnectorEventData.java`
- `react/branch/app/(dashboard)/connector/components/connector-status.tsx`
- `react/branch/app/(dashboard)/connector/hooks/connectorHooks.ts`
返回