visionA 雲端版前端 — 沿用 local-tool 既有 UI(原則 4:先抄 local-tool)+
新增雲端特有的登入 / 配對 / 設定流程,含以下整合階段:
- Phase 0:13 頁 + 30+ 元件 + 雛形 banner
- dashboard / devices / models / workspace / clusters / settings 等頁
- AppShell + Sidebar + Header + tokens + i18n(中英雙語 96 keys)
- API client + 5 stores + 3 hooks
- Phase 0.6:OIDC redirect 改造
- login 頁改為 OIDC redirect(`window.location.href = /api/auth/login`)
- register 改說明頁、account 改唯讀(user 資料來源是 MC)
- api client 改 cookie session(credentials: include)+ 完全清掉 localStorage
- Phase 0.7:stage 部署 + nil guard
- getApiBaseUrl() 修:browser 環境視為 same-origin(與 login 頁一致)
- login 頁加「已登入 → router.replace('/')」effect
- User type email/name 改 optional(MC id_token 不一定回 email/name claim)
- header.tsx UserMenu displayName 4 層 fallback:name → email → id → i18n
- 雛形 banner 文案更新(已接 Innovedus 帳號中心)+ 版號 Phase 0.7
驗證:pnpm lint / test (125/125) / build 全綠
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
235 lines
11 KiB
Markdown
235 lines
11 KiB
Markdown
# visionA Cloud Frontend
|
||
|
||
> visionA Cloud 前端 Web 應用(Next.js 16 + React 19 + Tailwind 4)。
|
||
>
|
||
> 目的:將 `local-tool/` 的使用體驗延伸到雲端,讓使用者透過瀏覽器操作由 local agent 連入的邊緣裝置(Kneron KL520/KL720 等)。
|
||
|
||
> 🚧 **Phase 0 雛形** — 任何功能皆以 UI 走通為目標,不做真實身分驗證、OAuth、WebSocket 即時推送、MJPEG 串流。完整功能清單見下方「雛形範圍與限制」。
|
||
>
|
||
> 規劃中的正式版請見 [`.autoflow/02-prd/PRD.md`](../.autoflow/02-prd/PRD.md),架構決策見 [`.autoflow/04-architecture/design-doc.md`](../.autoflow/04-architecture/design-doc.md)。
|
||
|
||
---
|
||
|
||
## 技術堆疊
|
||
|
||
| 層級 | 技術 | 版本 |
|
||
|------|------|------|
|
||
| 框架 | Next.js(App Router) | 16.1.6 |
|
||
| UI 函式庫 | React / React DOM | 19.2.3 |
|
||
| 語言 | TypeScript | ^5 |
|
||
| 樣式 | Tailwind CSS(含 `@tailwindcss/postcss`)| ^4 |
|
||
| 元件 primitive | Radix UI | ^1.4.3 |
|
||
| 元件規範 | shadcn `new-york` style(`components.json`) | — |
|
||
| 圖示 | lucide-react | ^0.575.0 |
|
||
| 狀態管理 | Zustand | ^5.0.11 |
|
||
| 主題 | next-themes | ^0.4.6 |
|
||
| Toast | sonner | ^2.0.7 |
|
||
| Class 工具 | clsx + tailwind-merge + class-variance-authority | — |
|
||
| 測試 | Vitest + @testing-library/react + jsdom | ^4 / ^16 / ^28 |
|
||
| Lint | ESLint + eslint-config-next | ^9 / 16.1.6 |
|
||
|
||
版本與 [`local-tool/frontend/`](../local-tool/frontend/) 對齊,確保「雲端版 / 離線版同一套前端」的架構決策可執行。
|
||
|
||
---
|
||
|
||
## 前置需求
|
||
|
||
- Node.js **≥ 20**
|
||
- pnpm **≥ 10**
|
||
- **依賴:** 需同時運行 [`visionA-backend`](../visionA-backend/README.md)(Go)。前端所有 API 呼叫會指向 `NEXT_PUBLIC_API_BASE`,後端未啟動時功能會顯示錯誤但 UI 仍可瀏覽(多數頁面對 501 / 連線錯誤已做 graceful fallback)。
|
||
|
||
---
|
||
|
||
## 快速啟動
|
||
|
||
```bash
|
||
# 1. 安裝依賴
|
||
cd visionA-frontend
|
||
pnpm install
|
||
|
||
# 2. 設定環境變數
|
||
cp .env.local.example .env.local
|
||
# 檢查 NEXT_PUBLIC_API_BASE 是否指到正確後端位址
|
||
|
||
# 3. 啟動後端(另一個 terminal)
|
||
cd ../visionA-backend
|
||
make run # 或參考 visionA-backend/README.md
|
||
|
||
# 4. 啟動前端
|
||
cd ../visionA-frontend
|
||
pnpm dev # http://localhost:3000
|
||
```
|
||
|
||
進入後**任何 email + 任何密碼**都可登入(Phase 0 的 StaticAuthProvider)。
|
||
|
||
### 可用腳本
|
||
|
||
| 指令 | 說明 |
|
||
|------|------|
|
||
| `pnpm dev` | 開發模式(Turbopack / HMR) |
|
||
| `pnpm build` | 產線打包(`output: "standalone"`,便於 Docker 部署) |
|
||
| `pnpm start` | 啟動 production build |
|
||
| `pnpm lint` | 執行 ESLint |
|
||
| `pnpm test` | 執行 Vitest(一次性) |
|
||
| `pnpm test:watch` | 執行 Vitest(watch mode) |
|
||
|
||
---
|
||
|
||
## 專案結構
|
||
|
||
```
|
||
visionA-frontend/
|
||
├── src/
|
||
│ ├── app/ # Next.js App Router 路由
|
||
│ │ ├── layout.tsx # Root layout(Theme / Locale / AppShell)
|
||
│ │ ├── page.tsx # / Dashboard
|
||
│ │ ├── login/ # /login
|
||
│ │ ├── register/ # /register(Phase 0 Coming Soon)
|
||
│ │ ├── account/ # /account 帳號設定 stub
|
||
│ │ ├── devices/ # /devices, /devices/[id], /devices/pair
|
||
│ │ ├── models/ # /models, /models/[id]
|
||
│ │ ├── workspace/ # /workspace, /workspace/[deviceId]
|
||
│ │ ├── clusters/ # /clusters(Phase 0 預告頁)
|
||
│ │ └── settings/ # /settings
|
||
│ ├── components/
|
||
│ │ ├── ui/ # Shadcn primitive(Button / Card / Input ...)
|
||
│ │ ├── layout/ # Sidebar / Header / PrototypeBanner / AppShell
|
||
│ │ ├── dashboard/ # StatCard / ActivityTimeline / ConnectedDevicesList
|
||
│ │ ├── devices/ # DeviceCard 相關
|
||
│ │ ├── models/ # ModelCard / ModelUploadDialog
|
||
│ │ ├── cloud/ # RemoteDeviceBadge
|
||
│ │ └── pairing/ # PairingTokenCard / PairingCountdown
|
||
│ ├── hooks/ # useFetch / useWebsocket / useTunnelStatus
|
||
│ ├── lib/
|
||
│ │ ├── api.ts # 統一 API client(envelope / 401 / 501 / timeout)
|
||
│ │ ├── utils.ts # cn() 等
|
||
│ │ └── i18n/ # zh-Hant / en 字典 + Context
|
||
│ ├── stores/ # Zustand stores(auth / session / device / model / activity / pairing)
|
||
│ ├── tests/ # Vitest setup
|
||
│ └── types/ # 共用型別(api / user / pairing ...)
|
||
├── public/ # 靜態資源
|
||
├── components.json # Shadcn CLI 設定
|
||
├── next.config.ts # output: standalone
|
||
├── tsconfig.json # @/* → ./src/*
|
||
└── package.json
|
||
```
|
||
|
||
---
|
||
|
||
## 主要頁面清單
|
||
|
||
| 路徑 | Phase 0 狀態 | 說明 |
|
||
|------|-------------|------|
|
||
| `/login` | ✅ 可用 | 任意帳密皆通過(StaticAuth) |
|
||
| `/register` | ⚠️ Coming Soon | 引導回 /login;Phase 1 才實作 |
|
||
| `/` Dashboard | ✅ 可用 | StatCard × 4、近期活動、快速操作;無裝置時顯示空狀態引導 /devices/pair |
|
||
| `/devices` | ✅ 可用 | 裝置列表;RemoteDeviceBadge 顯示遠端連線狀態 |
|
||
| `/devices/[id]` | ✅ 可用 | 裝置詳細、離線 banner |
|
||
| `/devices/pair` | ✅ 可用(核心) | 三步配對流程:產生 Pairing Token → 15 分鐘倒數 → 輪詢 3 分鐘 |
|
||
| `/models` | ✅ 可用 | 模型列表 + 上傳 Dialog(XHR 進度) |
|
||
| `/models/[id]` | ✅ 可用 | 模型詳細、部署到裝置 |
|
||
| `/workspace` | ✅ 可用 | 選擇線上裝置 |
|
||
| `/workspace/[deviceId]` | ⚠️ Camera placeholder | 推論 Start/Stop 可按;MJPEG stream Phase 1 接上 |
|
||
| `/clusters` | ⚠️ Phase 1 預告 | POC 有完整實作,雛形後端是 stub |
|
||
| `/account` | ✅ 可用 stub | 顯示使用者 + 登出;其他操作是 toast 提醒 |
|
||
| `/settings` | ✅ 可用 | 語言 / 主題 / API 端點切換 |
|
||
|
||
---
|
||
|
||
## 環境變數
|
||
|
||
| 變數 | 說明 | 預設值 | 必要 |
|
||
|------|------|--------|------|
|
||
| `NEXT_PUBLIC_API_BASE` | 雲端 API Server 位址(無尾斜線) | `http://localhost:3721` | 是 |
|
||
| `NEXT_PUBLIC_WS_BASE` | WebSocket 位址;留空則從 API 推導 | 從 API base 推導 | 否 |
|
||
|
||
所有 runtime 變數都以 `NEXT_PUBLIC_` 開頭(Next.js 要求前端可讀),**不要放真正機密的值**——這類值應走後端。
|
||
|
||
---
|
||
|
||
## Phase 0 雛形範圍與限制
|
||
|
||
### ✅ 可做到的
|
||
- 完整 UI 骨架(Sidebar / Header / 全域雛形 Banner / Sonner toast)
|
||
- i18n(zh-Hant / en)可即時切換,key 集合兩語系強制同步
|
||
- Light / Dark / System 主題切換
|
||
- 裝置 / 模型 / 活動 三個 store 的 CRUD UI(對齊 api-spec)
|
||
- **Pairing 流程**:產 token → 視覺切兩行顯示(複製永遠是完整 36 字元)→ 15 分鐘倒數(3 色階段)→ 輪詢連線狀態 → 成功 toast + 跳轉
|
||
- 離線降級:`RemoteDeviceBadge` + 頁面離線 Banner + Workspace 遮罩
|
||
- 模型上傳:XHR progress + presigned URL 直送 storage
|
||
|
||
### ❌ Phase 0 刻意不做(Phase 1 接手)
|
||
- **任何帳密可登入** — 後端 `StaticAuthProvider` 回 `demo-user`;重啟後端後前端 localStorage 的 token 雖然還在,但伺服器上沒有任何 session 資料
|
||
- **OAuth / 2FA / 密碼重設** — 全部 Phase 1
|
||
- **註冊功能** — 顯示 Coming Soon 頁面
|
||
- **重啟後端 → 裝置 / 模型資料消失** — 後端目前是 InMemoryRepository
|
||
- **WebSocket 即時推送** — 事件 / 推論結果 / 配對狀態的 WS endpoint 目前回 501,前端改用 3 秒輪詢(pairing 頁)
|
||
- **Camera / MJPEG 串流** — `/workspace/[deviceId]` 的 Camera tab 顯示 placeholder,Image / Video / Batch tabs disabled
|
||
- **Cluster CRUD** — `/clusters` 顯示 Phase 1 預告頁
|
||
- **Account CRUD** — 變更個人資料 / 刪除帳號都是 toast stub
|
||
- **真實 SHA-256 checksum** — 模型上傳用 `placeholder:{size}:{nameLen}`(詳見 `model-upload-dialog.tsx` §placeholderChecksum)
|
||
|
||
前端安全債詳見 [`.autoflow/04-architecture/security.md`](../.autoflow/04-architecture/security.md) §14(localStorage token / WS querystring / CSP / CSRF 等 6 項,每項有 Phase 1 計畫)。
|
||
|
||
---
|
||
|
||
## 效能預算(Phase 1 開始嚴格執行)
|
||
|
||
| 資源 | 預算 |
|
||
|------|------|
|
||
| JavaScript(首次載入,壓縮後) | < 200 KB |
|
||
| CSS(壓縮後) | < 50 KB |
|
||
| 首次載入總大小 | < 500 KB |
|
||
| Core Web Vitals | LCP < 2.5s、INP < 200ms、CLS < 0.1 |
|
||
|
||
雛形(Phase 0)階段僅確保 `pnpm build` 成功;CI 中加入 bundle 檢查由 DevOps 任務處理。
|
||
|
||
---
|
||
|
||
## 無障礙規範
|
||
|
||
目標 **WCAG 2.2 AA**(詳見 [`.autoflow/03-design/design-spec.md`](../.autoflow/03-design/design-spec.md) §8):
|
||
|
||
- 色彩對比:一般文字 ≥ 4.5:1、大文字 ≥ 3:1
|
||
- 鍵盤可操作所有核心流程(Tab / Enter / Escape)
|
||
- Focus ring 可見(shadcn 預設 `ring-2 ring-ring`)
|
||
- 語意化 HTML(`<nav>`、`<main>`、`<aside>`、`<header>`)
|
||
- 動態內容 `aria-live`
|
||
- Pairing token 顯示:`role="text"` + 完整 token 於 `aria-label`(避免 SR 一字一念)
|
||
- Status card:成功 / 失敗用 `role="status"` / `role="alert"` + `aria-live`
|
||
|
||
---
|
||
|
||
## 測試
|
||
|
||
- **策略**:Unit ~60% / Integration ~25% / Visual ~10% / E2E ~5%(E2E 交給 Testing Agent)
|
||
- **工具**:Vitest + React Testing Library + jsdom
|
||
- **當前**:15 個測試檔、94 個 test 全綠
|
||
- **強制一致**:`i18n.test.ts` 驗 zh-Hant / en 兩份字典 key 集合完全一致且無空字串
|
||
|
||
```bash
|
||
pnpm test # 一次跑完
|
||
pnpm test:watch # watch mode
|
||
```
|
||
|
||
---
|
||
|
||
## 部署
|
||
|
||
- `next.config.ts` 已設 `output: "standalone"`,方便未來 Docker 化
|
||
- Dockerfile / docker-compose / Helm chart 由 DevOps Agent 規劃(Phase 1)
|
||
|
||
---
|
||
|
||
## 相關文件
|
||
|
||
- PRD:[`.autoflow/02-prd/PRD.md`](../.autoflow/02-prd/PRD.md)
|
||
- 設計規格索引:[`.autoflow/03-design/design-spec.md`](../.autoflow/03-design/design-spec.md)
|
||
- Design Tokens:[`.autoflow/03-design/design-tokens.md`](../.autoflow/03-design/design-tokens.md)
|
||
- Pairing 流程:[`.autoflow/03-design/flows/flow-pairing.md`](../.autoflow/03-design/flows/flow-pairing.md)
|
||
- Auth 流程:[`.autoflow/03-design/flows/flow-auth.md`](../.autoflow/03-design/flows/flow-auth.md)
|
||
- TDD §10 前端章節:[`.autoflow/04-architecture/TDD.md`](../.autoflow/04-architecture/TDD.md)
|
||
- API Spec:[`.autoflow/04-architecture/api/api-spec.md`](../.autoflow/04-architecture/api/api-spec.md)
|
||
- 前端安全債:[`.autoflow/04-architecture/security.md`](../.autoflow/04-architecture/security.md) §14
|
||
- 整體進度:[`.autoflow/progress.md`](../.autoflow/progress.md)
|