# A17 EMS Backend Admin Dashboard 需求說明書

版本：v0.2 Draft
日期：2026-07-25
適用系統：`react/branch` + `java/ems_branch` + `ems_branch` database
Review 對象：Product / FE / Backend / QA / DevOps
文件狀態：需求草案，待確認 A17 production baseline 與權限範圍後進入實作

> 重要聲明：Mockup 中所有數字皆為假資料。這份文件已依現有 source code 與 repository 知識庫校準，但本次無法連上公開的 A17 staging endpoint，因此沒有把未驗證的 A17 數值寫成正式需求。第 12 節列出實作前必須完成的 A17 baseline 查核。

<!-- pagebreak -->

## 1. 執行摘要

### 1.1 問題

Branch Admin 的 `/dashboard` 目前是空白頁。`DashboardStatsCard` 雖然存在假資料版本，但在 page 中被註解，且資料模型只涵蓋電壓、電流、功率與簡化狀態，沒有對應現行 EMS 2.0 的 OCPP 連線、Connector runtime、輪充、離峰、帳務與資料新鮮度語意。

管理者現在必須分別進入充電站、Connector、Charge Group、帳單與事件頁面，才能回答下列基本問題：

- 案場現在是否正常？
- 哪些 Charge Point 離線、哪些 Connector 故障？
- 現在有多少充電工作階段，總負載是否接近契約容量？
- 輪充／離峰隊列是否阻塞或等待過久？
- 今日使用量與本月帳務是否有待處理項目？
- 畫面上的資料是否仍然新鮮、可被信任？

### 1.2 目標

建立「營運決策入口」，讓值班人員在 10 秒內判斷是否需要處理，並能從摘要直接進入既有管理頁面完成後續工作。Dashboard 第一版以讀取與導覽為主，不在首頁提供高風險設備控制。

### 1.3 V1 成功標準

| 指標 | 驗收目標 |
|---|---|
| 首屏判讀 | 使用者 10 秒內能指出是否有離線、故障、隊列阻塞或帳務異常 |
| API 效能 | `/api/dashboard/overview` 在 A17 資料量下 P95 小於 1.5 秒 |
| 資料新鮮度 | 即時區塊顯示 `generatedAt` 與各資料源 `updatedAt`；stale 不可被顯示成正常 |
| 可行動性 | 所有告警與關鍵數字都有明確 deep link |
| 穩定性 | 任一子查詢失敗時，其他區塊仍可顯示，並標記 partial data |
| 可及性 | 符合 WCAG 2.2 AA 的鍵盤、對比與 status message 基本要求 |

### 1.4 設計原則

- 先顯示異常，再顯示趨勢；先總覽，再逐層 drill-down。
- 顏色不是唯一狀態媒介，必須同時提供文字、icon 或形狀。
- 不把 OCPP connection、Charge Point runtime、Connector runtime、rotation queue 混成單一「在線／離線」。
- Dashboard 只呈現可解釋、可追溯、可採取行動的指標。
- 自動刷新頻率應符合資料變化速度，避免無意義的高頻 polling。

<!-- pagebreak -->

## 2. 現況盤點與 Gap Analysis

### 2.1 Source code 現況

| 項目 | 現況證據 | 結論 |
|---|---|---|
| Dashboard 頁面 | `react/branch/app/(dashboard)/dashboard/page.tsx` 只有空容器，`DashboardStatsCard` 被註解 | FE 需重新實作正式頁面 |
| 舊假資料元件 | `dashboard-stats-card.tsx` 內建 8 台假 Charger、固定電壓／電流／功率 | 僅可參考樣式，不可直接接 production |
| Connector 統計 | `GET /api/connector/dashboard` 回 total、available、charging、faulted、public、private | 可重用邏輯，但不足以支撐完整 Dashboard |
| 系統健康 | `SystemController.healthCheck()` 回 service、OCPP active connections、Charge Point count | 屬系統診斷 Map，未形成正式 typed Dashboard contract |
| Charge Point 連線 | `e_charge_points.ocpp_connection_status` 為 UNKNOWN / ONLINE / OFFLINE | 可作為連線健康的 primary source |
| Connector runtime | `e_connectors.status` 與 `last_status_change` | 可作為設備狀態與 stale 判定來源 |
| 輪充隊列 | `e_connector_priority` 有 priority、charge_status、status_reason、start_charging_time | 可提供等待、阻塞與派發摘要 |
| 容量配置 | `e_charge_group` 有 contract_capacity、allocated_power、reserved_slots | 可計算 slots 與容量摘要 |
| 即時量測 | `e_meter_values` 有 Power / Current / Energy 類型與 timestamp | 需新增高效的 latest-value aggregation |
| 帳務統計 | `/api/bills/statistics` 已能統計 total、paid、pending、overdue、energy、cost | 可重用查詢概念，需整併進 overview |
| 權限 | EMS 使用 SS3A role → function → api mapping | 新 API 必須加入 Dashboard function mapping |

### 2.2 已存在與需要新增

| 能力 | 可直接重用 | 需要新增／調整 |
|---|---|---|
| Connector 數量摘要 | `ConnectorRepository.queryStatisticsOfConnector()` | 增加 unavailable、suspended、preparing、stale 等語意 |
| OCPP 連線摘要 | `ChargePointService` / persisted connection status | 建立 typed DTO，區分 active session 與 persisted online |
| 即時負載 | MeterValue entity / repository | 新增 latest power query、單位正規化、stale cutoff |
| Charge Group 容量 | ChargeGroup + SlotCalculator 規則 | 新增唯讀 summary projection，避免複製錯誤公式 |
| 隊列健康 | ConnectorPriority entity | 新增等待時間、blocked reason、最久等待項目 |
| 今日用量 | Transaction / MeterValue 可計算 | 新增按時間區間的 aggregate query |
| 帳務摘要 | Bill statistics query | 併入同一 Dashboard response；定義月份與時區 |
| 告警列 | 各資料表已有部分訊號 | 新增 severity / code / count / actionUrl 的規則引擎 |
| FE 元件 | shadcn/ui、React Query、OpenAPI client | 新頁面、hooks、圖表、states、deep links、RWD |

### 2.3 不應直接沿用的舊假資料

- `systemVoltage` 與 `systemCurrent` 目前無單一「全站總表」資料源，不應以固定 220V 或把各 Connector 電流直接相加冒充真值。
- 「充電進度」與「預估剩餘時間」不是所有 OCPP 1.6 Charge Point 都能可靠提供，V1 不列為必備指標。
- `status = idle / maintenance` 與現有 `ConnectorStatus` enum 不一致，正式 UI 必須使用後端穩定 code。

<!-- pagebreak -->

## 3. 使用角色、場景與範圍

### 3.1 使用角色

| 角色 | 主要任務 | Dashboard 權限 |
|---|---|---|
| Site Admin / 管理者 | 查看全站狀況、處理設備與設定問題 | 完整唯讀總覽與 deep link |
| Operations / 值班人員 | 找出離線、故障、輪充阻塞與 stale data | 營運與告警區塊 |
| Finance / 帳務人員 | 查看本月待確認、逾期與結算狀態 | 帳務摘要與帳單 deep link |
| Engineer | 查看設備與通訊異常，再進 Engineer Tools 診斷 | 可見技術診斷 deep link，但首頁不直接下 command |

> 權限原則：前端隱藏不是授權。Dashboard API 必須由 SS3A function/API mapping 控制；未具 Dashboard function 的使用者不得直接呼叫。

### 3.2 核心使用場景

1. 使用者登入後進入 Dashboard。
2. 系統載入首屏 skeleton，取得單一 overview response。
3. 若有 Critical / Warning，告警列優先呈現，並依 severity、影響數量、持續時間排序。
4. 使用者查看 OCPP 連線、Connector 狀態、即時負載與隊列摘要。
5. 點擊卡片或告警進入既有 Charge Point、Connector、Charge Group、Bill 或 Engineer Tools 頁面。
6. 返回 Dashboard 時保留原本時間範圍，重新確認資料新鮮度。

### 3.3 V1 In Scope

- 案場單站總覽。
- OCPP connection、Charge Point、Connector、active session 統計。
- 即時總功率與近 24 小時負載序列。
- Charge Group slots、reserved、occupied、waiting、blocked 摘要。
- Connector 即時狀態卡與 detail drawer。
- 今日用電、近 30 日用量／session 趨勢。
- 本月帳務 pipeline 摘要。
- 告警列、deep link、資料新鮮度、partial-data state。
- Desktop / Tablet / Mobile responsive。

### 3.4 V1 Out of Scope

- 在 Dashboard 直接 Remote Start / Stop、Reset、Modbus 或 Engineer command。
- 告警 acknowledgement、owner assignment、SLA workflow。
- 跨多案場 HQ portfolio Dashboard。
- 預測性維護、AI 異常偵測、費用預測模型。
- 自建 BI 報表編輯器或讓使用者自由配置 widget。
- 取代既有 Charge Point、Connector、帳單、Engineer Tools 詳細頁。

<!-- pagebreak -->

## 4. 畫面資訊架構

![A17 Dashboard Mockup](a17-operations-dashboard-desktop.png)

### 4.1 首屏由上到下

| 區塊 | 使用者問題 | 主要行動 |
|---|---|---|
| Header | 我看的是哪個案場？資料何時更新？ | 刷新、切換時間範圍 |
| Needs Attention | 現在最需要處理什麼？ | 點擊進入對應清單 |
| KPI Row | 全站規模、在線、充電、等待、用量、帳務概況？ | 點擊卡片 drill-down |
| Live Load | 目前功率與契約容量的距離？ | 切 Live / 今日 / 7 日 |
| Group Capacity | 各群組 slot 是否不足或阻塞？ | 進 Charge Group |
| Connector Grid | 哪一支 Connector 現在是什麼狀態？ | 開 detail drawer |
| Usage Trend | 近 30 日 kWh 與 sessions 如何變化？ | 切 metric / range |
| Billing Pipeline | 本月帳務卡在哪個階段？ | 進 Billing / Invoice |

### 4.2 Connector detail drawer

至少顯示：

- connectorId、chargePointId、parkingSpaceNo、area、connectorType。
- OCPP connection status、Charge Point runtime、Connector runtime。
- errorCode、vendorErrorCode、info。
- currentTransactionId、開始時間、目前功率、累計用量。
- chargeGroup、queue status、priority、statusReason、waiting duration。
- lastHeartbeat、lastStatusChange、latestMeterAt。
- 「查看 Connector」、「查看 Charge Point」、「查看 Engineer Tools」deep link。

### 4.3 Responsive

| Breakpoint | 規格 |
|---|---|
| >= 1240 px | 固定側欄；6 張 KPI；Live Load 與 Group Capacity 雙欄 |
| 940 - 1239 px | 側欄縮合；KPI 三欄；圖表上下排列 |
| 640 - 939 px | KPI 兩欄；Connector 兩欄；表格改卡片 |
| < 640 px | KPI 兩欄或單欄；Connector 單欄；告警可水平滑動；不得出現頁面級水平捲軸 |

<!-- pagebreak -->

## 5. 指標字典與狀態定義

### 5.1 首要指標

| 指標 | 定義／公式 | Primary source | Freshness | 點擊後 |
|---|---|---|---|---|
| Charge Point 總數 | enabled CP count | `e_charge_points` | 60 秒 | `/cp/list` |
| OCPP Online | `ocpp_connection_status = ONLINE` | `e_charge_points` | 60 秒 | CP list filtered |
| Connector 可用數 | `status = AVAILABLE` | `e_connectors` | 60 秒 | Connector list filtered |
| 正在充電 | `status IN (CHARGING, SUSPENDED_EV, SUSPENDED_EVSE)`；卡片另顯 active transaction count | `e_connectors`, `e_transactions` | 30 秒 | Connector list |
| 等待供電 | queue status 為 eligible / not_plugged / blocked 的分項；不得只給單一合計 | `e_connector_priority` | 60 秒 | Charge Group |
| 即時總功率 | 各 enabled Connector 最新、未 stale 的 Power.Active.Import 加總；統一轉 kW | `e_meter_values` | 30 秒 | Connector grid |
| 契約容量利用率 | `currentPowerKw / sum(applicable contractCapacityKw) * 100`；分母規則須避免重複計算 | `e_charge_group` | 60 秒 | Charge Group |
| 今日用電量 | 台北時區今日已完成交易 + active transaction 即時增量 | `e_transactions`, `e_meter_values` | 5 分鐘 | Usage |
| 本月帳務待處理 | pending + overdue + invoice draft/pending 的分項 | settlement / billing tables | 15 分鐘 | Billing |
| 資料 stale 數 | lastHeartbeat / lastStatusChange / latestMeterAt 超過各自 cutoff | CP / Connector / MeterValue | 60 秒 | 告警列 |

### 5.2 三層狀態不可混用

| 狀態層 | 欄位／來源 | 顯示目的 |
|---|---|---|
| Connection | `ChargePoint.ocppConnectionStatus` | Branch 是否觀察到 OCPP 連線存活 |
| Runtime | `ChargePoint.status`, `Connector.status` | 設備／槍實際運作狀態 |
| Queue | `ConnectorPriority.chargeStatus`, `statusReason` | 離峰／輪充是否等待、充電、阻塞或完成 |

規則：

- CP OFFLINE 不代表每支 Connector 的最後 runtime status 可被改寫成 AVAILABLE。
- Heartbeat 或 BootNotification 只能證明連線存活，不能把 FAULTED／CHARGING 覆寫為 AVAILABLE。
- Connector 的 primary runtime owner 是 StatusNotification；畫面要分開顯示「OCPP 離線」與「最後設備狀態」。
- `currentTransactionId` 或 ACTIVE transaction 存在時，離線告警需標記「可能仍在離線充電／待 reconciliation」。

### 5.3 告警 severity

| Severity | 條件範例 | UI |
|---|---|---|
| Critical | CP OFFLINE 且有 active transaction；Connector FAULTED；資料全面 stale | 紅色、置頂、需 action |
| Warning | queue BLOCKED、等待超過門檻、帳單 overdue、容量利用率 >= 85% | 黃色、依持續時間排序 |
| Info | invoice draft、部分 Connector 未配置 tariff / charge group | 藍色、可收合 |

告警物件必須有 `code`、`severity`、`title`、`description`、`count`、`firstSeenAt`、`latestSeenAt`、`actionLabel`、`actionUrl`。

<!-- pagebreak -->

## 6. Functional Requirements

| ID | Priority | Owner | Requirement / Acceptance |
|---|---|---|---|
| FR-001 | Must | BE | 提供 typed `GET /api/dashboard/overview`；成功回 `generatedAt`, `dataStatus`, 各區塊資料 |
| FR-002 | Must | FE | `/dashboard` 顯示正式營運總覽，不再引用 component 內 hard-coded production data |
| FR-003 | Must | BE | 統計 OCPP connection 與 CP runtime，兩者使用不同欄位 |
| FR-004 | Must | BE | 統計 Connector 全部主要狀態，不只 AVAILABLE / CHARGING / FAULTED |
| FR-005 | Must | BE | 取得 latest power 並做 unit normalization；stale 資料不納入總功率 |
| FR-006 | Must | BE | 依既有 SlotCalculator 語意產生每個 Charge Group 的 total / occupied / reserved / available slots |
| FR-007 | Must | BE | 回傳 queue waiting / blocked / charging 分項與最久等待時間 |
| FR-008 | Must | BE | 回傳今日用量與近 30 日 kWh / sessions 時序 |
| FR-009 | Must | BE | 回傳本月 billing / invoice pipeline，不混用 BillStatus 與 InvoiceStatus |
| FR-010 | Must | BE | 建立 severity-first alerts，所有 alert 具 deep link |
| FR-011 | Must | FE | 所有區塊支援 loading / empty / error / stale / partial data |
| FR-012 | Must | FE | Header 顯示最後成功更新時間；手動 Refresh 不重設使用者選擇 |
| FR-013 | Must | FE | Connector card 點擊開 drawer；首頁禁止直接發送控制 command |
| FR-014 | Must | FE | status 以 code 判斷邏輯，label 僅顯示；未知值不可 fallback 成 AVAILABLE |
| FR-015 | Must | BE | Dashboard API 納入 SS3A API/function mapping，Operator identity 由 JWT |
| FR-016 | Should | BE/FE | API 子區塊失敗時回 partial status；FE 保留成功區塊並允許 retry |
| FR-017 | Should | FE | Live / 今日 / 7 日與 30 日趨勢切換不得觸發不必要的全頁刷新 |
| FR-018 | Should | FE | URL query 保存 range / metric，使 deep link 可重現畫面 |
| FR-019 | Should | BE | 每個 aggregate query 記錄 elapsed time；慢查詢可被 observability 追蹤 |
| FR-020 | Could | FE | 匯出目前 Dashboard snapshot；V1 可先不做 |

### 6.1 UI States 驗收

- Loading：使用區塊 skeleton，不使用全頁 blocking spinner。
- Empty：說明為何沒有資料與可採取動作，不只顯示 `No data`。
- Error：顯示失敗區塊與 retry；不可把錯誤顯示成 0。
- Stale：保留最後值但標示 stale 與時間，不納入需要即時性的合計。
- Partial：Header 顯示「部分資料未更新」，各區塊顯示自己的 source status。
- Unauthorized：API 回 403；FE 顯示無權限頁，不 silently hide failure。

<!-- pagebreak -->

## 7. API Contract 建議

### 7.1 Endpoint

`GET /api/dashboard/overview?range=TODAY&trendDays=30`

Query：

| 參數 | 值 | Default | 說明 |
|---|---|---|---|
| `range` | LIVE / TODAY / SEVEN_DAYS | TODAY | 負載序列範圍 |
| `trendDays` | 7 / 30 / 90 | 30 | 用量趨勢日數 |
| `timezone` | IANA timezone | Asia/Taipei | V1 可固定並在 response 回傳 |

### 7.2 Response shape

```json
{
  "generatedAt": "2026-07-25T16:30:00+08:00",
  "timezone": "Asia/Taipei",
  "dataStatus": "COMPLETE",
  "sourceStatuses": [
    {"source": "OCPP", "status": "FRESH", "updatedAt": "..."},
    {"source": "METER_VALUES", "status": "STALE", "updatedAt": "..."}
  ],
  "alerts": [],
  "networkHealth": {
    "chargePointTotal": 0,
    "ocppOnline": 0,
    "ocppOffline": 0,
    "activeWebSocketSessions": 0
  },
  "connectorSummary": {
    "total": 0,
    "available": 0,
    "charging": 0,
    "preparing": 0,
    "suspended": 0,
    "unavailable": 0,
    "faulted": 0
  },
  "liveLoad": {
    "currentPowerKw": null,
    "contractCapacityKw": null,
    "utilizationPercent": null,
    "latestMeterAt": null,
    "series": []
  },
  "chargeGroups": [],
  "connectors": [],
  "usageTrend": [],
  "billingPipeline": {}
}
```

### 7.3 Contract 規則

- 金額使用 decimal string 或明確 BigDecimal schema，不使用浮點近似。
- 時間一律 ISO-8601 含 offset；response 額外回 timezone。
- 數值不存在用 `null`，不可用 0 表示「未知」。
- `dataStatus`：COMPLETE / PARTIAL / STALE。
- source status：FRESH / STALE / ERROR / NOT_CONFIGURED。
- 狀態 code 穩定且供 FE 邏輯使用；中文 label 可由後端提供，但不得取代 code。
- 每個 DTO class、public method 與 DTO 欄位依專案標準補繁體中文用途說明與 OpenAPI `@Schema`。

### 7.4 Error handling

| HTTP | 情況 |
|---|---|
| 200 | 完整、partial 或 stale，細節由 `dataStatus` 表示 |
| 400 | query parameter 不合法 |
| 401 | 未登入／token 無效 |
| 403 | 無 Dashboard function |
| 500 | overview 無法建立任何可用內容 |
| 503 | 服務層整體不可用；不可用來表示單一子資料源失敗 |

<!-- pagebreak -->

## 8. Backend 開發內容

### 8.1 建議 package

```text
api/dashboard/
  DashboardController.java
  DashboardService.java
  DashboardAlertService.java
  dto/
    DashboardOverviewDto.java
    NetworkHealthDto.java
    ConnectorSummaryDto.java
    LiveLoadDto.java
    ChargeGroupSummaryDto.java
    DashboardConnectorDto.java
    UsageTrendPointDto.java
    BillingPipelineDto.java
    DashboardAlertDto.java
```

Repository 可新增 projection query，但不應把所有邏輯塞進一條巨大 native SQL。建議由 `DashboardService` 在同一 read-only transaction 內協調 5 - 7 個有界 aggregate query，並獨立量測 elapsed time。

### 8.2 BE Task Breakdown

| Task | 說明 | Done Definition |
|---|---|---|
| BE-01 Contract | 建立 Controller、DTO、OpenAPI schema | schema 可由 `yarn openapi:refresh` 產生 |
| BE-02 Network health | CP connection / runtime aggregate | 單元測試涵蓋 ONLINE、OFFLINE、UNKNOWN |
| BE-03 Connector summary | 全狀態 aggregate + active transaction count | 狀態總和可對回 total |
| BE-04 Latest load | latest power query、unit convert、stale cutoff | W/kW 混合與 stale 測試通過 |
| BE-05 Group capacity | 使用既有 slot rule；回 waiting / blocked | reserved slot 不重複計算 |
| BE-06 Usage trend | 今日與日粒度 aggregate | Asia/Taipei 日界線測試 |
| BE-07 Billing | 整合 settlement / billing / invoice status | 不混淆兩套 enum |
| BE-08 Alerts | 規則與 deep link mapping | severity ordering deterministic |
| BE-09 Partial data | 子查詢隔離與 source status | 單一 repository exception 仍回 200 PARTIAL |
| BE-10 Security | SS3A function/API migration | 無權限 403、有權限 200 |
| BE-11 Performance | A17 EXPLAIN、P95 測試、必要 index proposal | P95 < 1.5 秒 |
| BE-12 Documentation | 更新 `ai/`、OpenAPI、註解 | 與實作一致 |

### 8.3 Database 原則

V1 原則上不新增 Dashboard snapshot table，先以 read-only aggregate 驗證 A17 資料量與查詢成本。若 EXPLAIN 證明 latest MeterValue 查詢昂貴，再提出普通 index 或彙總表方案。

候選 index 必須先用 A17 EXPLAIN 驗證：

- `e_meter_values(connector_id, value_type, timestamp)`
- `e_transactions(status, start_timestamp)`
- `e_connector_priority(charge_group, charge_status, priority)`

依專案規則，不為 Dashboard 新增 UNIQUE / FOREIGN KEY / CHECK / cascade constraint。

### 8.4 快取與刷新

- FE 預設 60 秒 refetch；使用者可手動刷新。
- Live Load 若需要 30 秒，可只刷新 live 子查詢；第一版可先整包 60 秒。
- BE 可使用 15 - 30 秒短快取，但 cache key 必須包含 range / trendDays。
- 快取失敗不可回舊值而不標示；必須保留 generatedAt / source updatedAt。

<!-- pagebreak -->

## 9. Frontend 開發內容

### 9.1 建議目錄

```text
app/(dashboard)/dashboard/
  page.tsx
  hooks/useDashboardOverview.ts
  lib/dashboard-presentation.ts
  components/
    dashboard-header.tsx
    attention-strip.tsx
    kpi-card.tsx
    live-load-chart.tsx
    charge-group-capacity.tsx
    connector-status-grid.tsx
    connector-detail-drawer.tsx
    usage-trend-chart.tsx
    billing-pipeline.tsx
    dashboard-section-state.tsx
```

### 9.2 FE Task Breakdown

| Task | 說明 | Done Definition |
|---|---|---|
| FE-01 Types | refresh OpenAPI，禁止手改 `emsapi.ts` | build 通過 |
| FE-02 Query hook | React Query hook、range key、refetch policy | 不重複 request |
| FE-03 Header | 案場、generatedAt、dataStatus、refresh | refresh 有狀態回饋 |
| FE-04 Alerts | severity ordering、deep link | keyboard 可操作 |
| FE-05 KPI | 共用 KpiCard，null / 0 / stale 分開 | 長 label 不破版 |
| FE-06 Charts | load / usage，tooltip、單位、文字摘要 | 空資料與單點資料正常 |
| FE-07 Group | slots、reserved、occupied、waiting | 不只用顏色 |
| FE-08 Connector | status grid + drawer + deep link | unknown status 安全顯示 |
| FE-09 Billing | pipeline 與既有 Billing route 對接 | enum label 正確 |
| FE-10 States | loading / empty / error / stale / partial / 403 | Story/fixture 可重現 |
| FE-11 RWD | 四個 breakpoint | 無頁面級 horizontal scroll |
| FE-12 Accessibility | focus、ARIA status、contrast、keyboard | 基本 axe / manual check 通過 |

### 9.3 Fixture 策略

- 假資料集中在 `dashboard.fixtures.ts` 或 MSW fixture，不放在 component body。
- Fixture shape 必須完全等同 OpenAPI generated type。
- 至少準備：healthy、critical、partial、stale、empty、large-data 六組 scenario。
- Production build 不得在 API error 時自動 fallback 到 fixture。

### 9.4 圖表要求

- X/Y 軸必須有時間與單位。
- Tooltip 可鍵盤／觸控使用。
- 圖表旁提供最新值與趨勢文字摘要，不能只提供視覺線條。
- 不同單位不放在同一 Y 軸；若需要雙軸，必須清楚標示。
- Loading 時維持固定高度，避免 layout shift。

<!-- pagebreak -->

## 10. Non-Functional Requirements

### 10.1 Performance

- Overview response gzip 後建議小於 250 KB。
- 初次可見內容在一般內網環境 2.5 秒內呈現。
- A17 production P95 API 小於 1.5 秒；P99 小於 3 秒。
- Trend points 做 server-side downsampling；單一 series 不超過 500 points。
- 禁止 N+1 查詢 Connector latest meter values。

### 10.2 Reliability

- 任一子區塊失敗不可把值改成 0。
- React Query 保留最後成功資料時，必須顯示 stale / fetching。
- 手動 refresh 可取消前一個 in-flight request。
- API response 必須具 deterministic ordering，避免卡片跳動。

### 10.3 Security / Privacy

- 使用既有 JWT 與 SS3A function mapping。
- Dashboard 不回住戶姓名、手機、LINE ID 等個資。
- Connector drawer 只回營運必要欄位。
- 不回 raw OCPP payload、credential、token 或 Modbus connection details。
- Deep link 目的頁仍需自己的後端權限檢查。

### 10.4 Accessibility

- 依 WCAG 2.2 AA：狀態不可只靠顏色，文字對比、focus visible、status message 可被 assistive technology 判定。
- 圖表有文字替代摘要。
- 自動更新不搶 focus；更新完成用 non-intrusive status message。
- Touch target 建議至少 24 × 24 CSS px，主要操作建議 44 × 44。

### 10.5 Observability

- 記錄 overview total elapsed、每個子查詢 elapsed、dataStatus、result counts。
- 不記錄完整 response、個資或敏感資料。
- 慢查詢與 partial response 應有可搜尋的 structured log。
- 後續可增加 Dashboard API metrics：request count、P95、error rate、partial rate。

<!-- pagebreak -->

## 11. QA、驗收與發布計畫

### 11.1 Backend Test

- Repository integration：狀態 aggregate、latest meter、date boundary、billing status。
- Service unit：severity、stale cutoff、unit normalization、partial failure。
- Security：401 / 403 / 200。
- Contract：OpenAPI field、enum、nullability、ISO time。
- Performance：A17 baseline data volume 下執行 EXPLAIN 與 repeated request。

### 11.2 Frontend Test

- fixture scenarios：healthy / critical / partial / stale / empty / large。
- viewport：1440、1024、768、390 px。
- keyboard：alert、range toggle、connector card、drawer、deep link。
- unknown enum、超長 ID、0、null、極大值、慢回應。
- chart single point、cross-day、timezone、30 日資料。

### 11.3 End-to-End Acceptance

1. CP 斷線後，Dashboard 在刷新週期內顯示 OFFLINE，且不把最後 Connector runtime 誤顯示為 AVAILABLE。
2. Connector FAULTED 會產生 Critical alert，點擊後進入對應 Connector。
3. ACTIVE transaction 期間可看到 session 與功率；MeterValue stale 後功率不納入 live total。
4. Queue BLOCKED 顯示 reason 與等待時間，點擊後進 Charge Group。
5. Billing pending / overdue 與 Invoice draft / pending 分開顯示。
6. 任一資料源模擬 exception 時，其餘區塊仍可使用並顯示 PARTIAL。
7. 未授權帳號直接呼叫 API 得到 403。

### 11.4 Rollout

| Phase | 內容 | Gate |
|---|---|---|
| 0 - Baseline | 執行 A17 queries、確認指標與門檻 | Product / BE 確認 |
| 1 - Contract | BE DTO + OpenAPI + fixture | FE / BE contract review |
| 2 - Backend | aggregate、alerts、security、tests | API test / P95 |
| 3 - Frontend | layout、states、RWD、accessibility | FE review |
| 4 - Staging | 使用真實 staging data E2E | QA sign-off |
| 5 - A17 | feature flag / controlled release | 24 小時觀察 |

回滾：保留舊空 Dashboard route 的 deploy rollback 能力；若 overview API 異常，FE 顯示明確 error，不導向假資料。

<!-- pagebreak -->

## 12. A17 Data Baseline 與待確認事項

### 12.1 實作前必跑 baseline

正式開發前，BE 需在 A17 以 read-only query 取得：

| Baseline | 用途 |
|---|---|
| CP 數量與 ONLINE / OFFLINE / UNKNOWN 分布 | 決定 network KPI 與 offline alert |
| Connector 各 status、area、type 分布 | 驗證卡片狀態與 grid 規模 |
| active transaction 數與最長持續時間 | 驗證 session 與離線保護 |
| 每個 Charge Group 的 contract / allocated / reserved / queue | 驗證 slots 與等待邏輯 |
| MeterValue 24 小時筆數、每分鐘峰值、latest lag | 決定 query / index / downsampling |
| 近 30 日 transaction 數、kWh、日分布 | 驗證 trend |
| 當月 billing / invoice status 分布 | 驗證 pipeline |
| Table row count 與 EXPLAIN | 確認 P95 目標可行 |

### 12.2 建議 read-only SQL

```sql
SELECT ocpp_connection_status, status, COUNT(*)
FROM e_charge_points
GROUP BY ocpp_connection_status, status;

SELECT status, connector_area, connector_type, COUNT(*)
FROM e_connectors
GROUP BY status, connector_area, connector_type;

SELECT charge_group, charge_status, status_reason, COUNT(*),
       MIN(create_time) AS oldest_waiting_at
FROM e_connector_priority
GROUP BY charge_group, charge_status, status_reason;

SELECT value_type, unit, COUNT(*), MAX(timestamp) AS latest_at
FROM e_meter_values
WHERE timestamp >= NOW() - INTERVAL 24 HOUR
GROUP BY value_type, unit;

SELECT status, COUNT(*), SUM(energy_consumed) / 1000 AS energy_kwh
FROM e_transactions
WHERE start_timestamp >= CURDATE() - INTERVAL 30 DAY
GROUP BY status;

SELECT bill_status, COUNT(*), SUM(energy_usage), SUM(cost_predict)
FROM e_bill_settlement
WHERE year = YEAR(CURDATE()) AND month = MONTH(CURDATE())
GROUP BY bill_status;
```

### 12.3 需要 Product / Engineering 決策

| ID | 問題 | 建議預設 |
|---|---|---|
| D-01 | Dashboard 僅 A17 單站，或未來需同畫面切多社區？ | V1 單站，API 保留 buildingId 擴充空間 |
| D-02 | 即時負載以 Charge Group 契約容量總和為分母是否會重複？ | 先依 A17 電力拓撲確認後再定 |
| D-03 | CP OFFLINE 告警門檻是否沿用 330 秒 watchdog？ | 沿用 persisted status，不在 Dashboard 另推導 |
| D-04 | Queue waiting warning 門檻？ | 先以 rotation cycle × 2，baseline 後調整 |
| D-05 | 帳務資料顯示 Bill、Settlement 或 Invoice 哪一層為主？ | 首頁顯示 pipeline 分層，不合併成單一數字 |
| D-06 | Dashboard function 要授權哪些既有 role？ | 明確 mapping，不自動繼承 Engineer 能力 |
| D-07 | 是否需要 30 秒 live refresh？ | V1 60 秒，確認負載與 DB 成本後再縮短 |

<!-- pagebreak -->

## 13. Definition of Done

本需求只有在以下條件全部達成時才算完成：

- Product 確認本文件 scope、KPI 字典與 decisions。
- A17 baseline 已完成，結果沒有推翻 KPI 公式或效能方案。
- Backend overview API、OpenAPI、SS3A mapping、測試完成。
- FE 完成所有區塊、states、RWD、accessibility 與 deep link。
- Mock fixture 不會在 production error 時被使用。
- A17 staging E2E 與 P95 驗證通過。
- `ai/02-backend-services.md`、`ai/03-database.md`、`ai/04-frontend.md` 依實作更新。
- FE / BE / QA review issue 皆有對應 acceptance evidence。
- 發布後 24 小時監看 error rate、partial rate 與 query latency。

## 14. 參考來源

- EMS source code：`react/branch/app/(dashboard)/dashboard/`、`java/ems_branch/.../ConnectorController.java`、`ConnectorRepository.java`、`SystemController.java`、Charge Group / MeterValue / Billing 相關 Entity 與 Repository。
- EMS AI knowledge base：`ai/01-overview.md`、`ai/02-backend-services.md`、`ai/03-database.md`、`ai/04-frontend.md`、`ai/06-domain-glossary.md`。
- Grafana Dashboard Best Practices：<https://grafana.com/docs/grafana/latest/visualizations/dashboards/build-dashboards/best-practices/>
- W3C WCAG 2.2 - Use of Color：<https://www.w3.org/WAI/WCAG22/Understanding/use-of-color>
- W3C WCAG 2.2 - Status Messages：<https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html>
- Open Charge Alliance OCPP 1.6 resources：<https://openchargealliance.org/protocols/ocpp-protocols/ocpp-1-6/>
