diff --git a/visionA-frontend/.env.local.example b/visionA-frontend/.env.local.example new file mode 100644 index 0000000..c234e6c --- /dev/null +++ b/visionA-frontend/.env.local.example @@ -0,0 +1,38 @@ +# visionA Cloud Frontend — 環境變數範本 +# +# 複製一份為 `.env.local`,並依實際環境填入。 +# `.env.local` 由 .gitignore 忽略,**切勿 commit**。 +# +# 所有 runtime 變數都以 `NEXT_PUBLIC_` 開頭(Next.js 要求前端可讀)。 +# ⚠️ 因此不要放真正機密的值——這類值應走後端 API。 + +# 後端 API server base URL(不含 trailing slash) +# 預設對應 visionA-backend 本地開發 port(TDD §1.4 api-server 預設 3001; +# F5 雛形階段允許以 3721 對應 local-tool 既有後端,以方便整合測試) +# +# ⚠️ BFF Pattern(OF2 / Phase 0.6 OIDC 之後): +# - 所有 API 請求帶 `credentials: 'include'`,瀏覽器會自動攜帶 backend 設的 +# `visiona_session` HttpOnly cookie。 +# - 跨 origin(本範例:frontend localhost:3000 ↔ backend localhost:3721)時, +# backend CORS 必須回傳: +# * `Access-Control-Allow-Credentials: true` +# * `Access-Control-Allow-Origin: http://localhost:3000`(明確 origin,**禁止 `*`**) +# - 上述條件任一不符 → 瀏覽器會在 console 報 CORS 錯誤、fetch 拋 NetworkError。 +# - 同 origin 部署(prod 通常將 frontend / backend 放同網域)時無此限制。 +NEXT_PUBLIC_API_BASE=http://localhost:3721 + +# WebSocket base URL(通常是 API base 改 ws/wss;同源部署時可留空讓前端自動推導) +NEXT_PUBLIC_WS_BASE=ws://localhost:3721 + +# Innovedus Member Center 註冊頁 URL(Phase 0.6 OIDC 接入;對齊 oidc-tdd.md §10.1 / §10.5) +# - login 頁的「前往註冊」連結會指向這裡(在新分頁開啟) +# - register 頁的「前往 Innovedus 帳號中心」按鈕也會跳到這裡(同分頁完整導航) +# - 未設定(或留空)→ 兩處 UI 均會 disable(aria-disabled="true") +# - dev 預設指向 Member Center 本地 dev port 5050 +NEXT_PUBLIC_MEMBER_CENTER_REGISTER_URL=http://localhost:5050/account/register + +# Innovedus Member Center profile 頁 URL(Phase 0.6 OIDC 接入;對齊 oidc-tdd.md §10.3) +# - /account 頁「前往 Innovedus 帳號中心」按鈕會指向這裡(在新分頁開啟) +# - 未設定(或留空)→ 按鈕會被替換為「未設定」hint +# - dev 預設指向 Member Center 本地 dev port 5050 +NEXT_PUBLIC_MEMBER_CENTER_PROFILE_URL=http://localhost:5050/account/profile diff --git a/visionA-frontend/.gitignore b/visionA-frontend/.gitignore new file mode 100644 index 0000000..82991c6 --- /dev/null +++ b/visionA-frontend/.gitignore @@ -0,0 +1,44 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ +/dist/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files(可依需要 opt-in 提交,例如 .env.example / .env.local.example) +.env* +!.env.example +!.env.local.example + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/visionA-frontend/README.md b/visionA-frontend/README.md new file mode 100644 index 0000000..c0810d8 --- /dev/null +++ b/visionA-frontend/README.md @@ -0,0 +1,234 @@ +# 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(`