From e02059eff28943b21e57d253f005de6b2c71e6c1 Mon Sep 17 00:00:00 2001 From: jim800121chen Date: Mon, 4 May 2026 13:56:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(visionA-frontend):=20Phase=200.8=20convers?= =?UTF-8?q?ion=20UI=20=E2=80=94=20sidebar=20tab=20+=206=20view=20+=205=20e?= =?UTF-8?q?2e=20flow=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 0.8 對應後端轉檔功能的 frontend UI。完整 state machine(idle / uploading / queued / running / succeeded / failed / expired)+ XHR upload progress + polling + half-auto result handling(加到模型庫 / 下載)。 新增 src/app/conversion/(11 元件 + 12 test files,~5,000 行 prod+test,310/310 tests 全綠): - page.tsx:state router(mount → bootstrap → 依 store.uiState 切換 view)+ toast on store.error(aborted / active_job_exists 不 toast,避免雙重提示) - IdleForm.tsx + FileDropzone.tsx + ChipSelector.tsx:上傳表單(拖放 + .onnx/.tflite format 驗證 + 500MB 大小限制 + KL520/630/720/730 chip 選擇 + ref images ≤100×10MB) - UploadingView.tsx:XHR upload progress 0-100% + EWMA ETA(alpha=0.3 平滑,<5s 顯示 「即將完成」避抖動)+ beforeunload warning + AlertDialog 取消 - ProcessingView.tsx:StageIndicator 三段式(解析模型 → 量化編譯 → 編譯 NEF)+ Progress bar 三模式(queued / determinate / indeterminate)+ tab title `(轉檔中)` 防疊加 + 409 active_job_exists banner(switchedFromActiveJob flag) - SuccessView.tsx + PromoteDialog.tsx:兩按鈕(加到模型庫 / 下載)視覺平衡不暗示預設答案 + 7 天 expires_at 倒數(mount 時 setTimeout 鎖過期那刻 + 60s tick)+ PromoteDialog 單欄位 name 驗證(≤100 字元 / 無 /\\)+ spinner-during-close 阻擋 + 409 already_imported 特殊 訊息 + toast.success router.push 跳模型清單 - FailedView.tsx:5 個 known error code 翻譯(UNSUPPORTED_FORMAT / INVALID_CHECKSUM / QUANTIZATION_FAILED / MODEL_TOO_LARGE / QUOTA_EXCEEDED)+ unknown fallback + 3 個建議解決方法 + Job ID 前 8 字元(供回報)+ 「重新開始」 - ExpiredView.tsx:橘色 hero(過期不 alarming)+ source/chip 摘要 + 「重新轉檔」→ store.reset() 新增 src/stores/conversion-store.ts(Zustand store + 29 tests): - 7 個 UI state machine(不允許跳階段) - recursive setTimeout polling(running 5s / queued 10s / 5xx 指數退避 cap 30s)+ visibilitychange 暫停/恢復 - 不持久化 jobId(純靠 backend getActiveConversion() lazy rebuild ownership) - AbortController 防 stale request + 取消上傳 - switchedFromActiveJob flag(409 自動切到既存 job + UI 顯示 banner) - formDraft(chip / taskName 提到 store,failed→idle 後保留設定,file picker 重選; File 物件不能序列化只留 local) 新增 src/lib/api/conversion.ts + types/conversion.ts(5 client 函式 + 22 tests): - initConversion:XHR multipart + onUploadProgress + AbortSignal - getActiveConversion / getConversion / promoteConversionToModels:標準 fetch - getConversionDownloadURL:純函式,回 `/api/conversion/{job_id}/download` 給 anchor download 觸發(server-side 302 → FAA,token 不過 frontend JS) - ConversionAPIError(status, code, message, requestId?),code 統一全小寫對齊 conversion.error. i18n key 命名 新增 src/lib/utils/eta.ts(EWMA 演算法純函式 + 19 tests):抽出 smoothSpeed / estimateRemainingSeconds / instantSpeedBytesPerSec / computeEtaUpdate(test 友善)。 新增 src/app/conversion/e2e-conversion-flow.test.tsx(5 e2e flow tests): - happy path .onnx + KL720 + 0 ref images(idle → upload → polling → succeeded → 加到模型庫) - variant 5 ref images - upload fail → retry(form 設定保留) - polling 5xx 指數退避 → 恢復繼續 - expired job → ExpiredView → 重新轉檔 修改 sidebar.tsx:左側 nav 新增「轉檔」tab(Wand2 icon,模型庫之後)。 修改 i18n 字典:新增 ~150 個 conversion.* keys(中英雙語對齊)。 PRD §9 14 條功能驗收條件全達成(4 條整合驗收等 stage 部署)。 驗證:pnpm lint / test (310/310) / build 全綠。 對齊文件: - .autoflow/02-prd/features/feature-converter-integration.md - .autoflow/03-design/wireframes/wireframe-conversion.md - .autoflow/03-design/flows/flow-conversion.md - .autoflow/04-architecture/api/api-conversion.md Co-Authored-By: Claude Opus 4.7 (1M context) --- .../conversion/components/ChipSelector.tsx | 86 ++ .../components/ExpiredView.test.tsx | 139 +++ .../app/conversion/components/ExpiredView.tsx | 139 +++ .../conversion/components/FailedView.test.tsx | 314 +++++++ .../app/conversion/components/FailedView.tsx | 274 ++++++ .../conversion/components/FileDropzone.tsx | 166 ++++ .../conversion/components/IdleForm.test.tsx | 248 +++++ .../app/conversion/components/IdleForm.tsx | 438 +++++++++ .../components/ProcessingView.test.tsx | 464 +++++++++ .../conversion/components/ProcessingView.tsx | 493 ++++++++++ .../conversion/components/PromoteDialog.tsx | 318 +++++++ .../components/SuccessView.test.tsx | 534 +++++++++++ .../app/conversion/components/SuccessView.tsx | 540 +++++++++++ .../components/UploadingView.test.tsx | 263 ++++++ .../conversion/components/UploadingView.tsx | 402 ++++++++ .../conversion/e2e-conversion-flow.test.tsx | 615 ++++++++++++ .../src/app/conversion/page.test.tsx | 368 ++++++++ visionA-frontend/src/app/conversion/page.tsx | 150 +++ .../src/components/layout/sidebar.test.tsx | 9 +- .../src/components/layout/sidebar.tsx | 5 + .../src/lib/api/conversion.test.ts | 552 +++++++++++ visionA-frontend/src/lib/api/conversion.ts | 442 +++++++++ .../src/lib/i18n/dictionaries/en.ts | 283 ++++++ .../src/lib/i18n/dictionaries/zh-Hant.ts | 263 ++++++ visionA-frontend/src/lib/utils/eta.test.ts | 170 ++++ visionA-frontend/src/lib/utils/eta.ts | 156 ++++ .../src/stores/conversion-store.test.ts | 881 ++++++++++++++++++ .../src/stores/conversion-store.ts | 754 +++++++++++++++ visionA-frontend/src/types/conversion.ts | 102 ++ 29 files changed, 9565 insertions(+), 3 deletions(-) create mode 100644 visionA-frontend/src/app/conversion/components/ChipSelector.tsx create mode 100644 visionA-frontend/src/app/conversion/components/ExpiredView.test.tsx create mode 100644 visionA-frontend/src/app/conversion/components/ExpiredView.tsx create mode 100644 visionA-frontend/src/app/conversion/components/FailedView.test.tsx create mode 100644 visionA-frontend/src/app/conversion/components/FailedView.tsx create mode 100644 visionA-frontend/src/app/conversion/components/FileDropzone.tsx create mode 100644 visionA-frontend/src/app/conversion/components/IdleForm.test.tsx create mode 100644 visionA-frontend/src/app/conversion/components/IdleForm.tsx create mode 100644 visionA-frontend/src/app/conversion/components/ProcessingView.test.tsx create mode 100644 visionA-frontend/src/app/conversion/components/ProcessingView.tsx create mode 100644 visionA-frontend/src/app/conversion/components/PromoteDialog.tsx create mode 100644 visionA-frontend/src/app/conversion/components/SuccessView.test.tsx create mode 100644 visionA-frontend/src/app/conversion/components/SuccessView.tsx create mode 100644 visionA-frontend/src/app/conversion/components/UploadingView.test.tsx create mode 100644 visionA-frontend/src/app/conversion/components/UploadingView.tsx create mode 100644 visionA-frontend/src/app/conversion/e2e-conversion-flow.test.tsx create mode 100644 visionA-frontend/src/app/conversion/page.test.tsx create mode 100644 visionA-frontend/src/app/conversion/page.tsx create mode 100644 visionA-frontend/src/lib/api/conversion.test.ts create mode 100644 visionA-frontend/src/lib/api/conversion.ts create mode 100644 visionA-frontend/src/lib/utils/eta.test.ts create mode 100644 visionA-frontend/src/lib/utils/eta.ts create mode 100644 visionA-frontend/src/stores/conversion-store.test.ts create mode 100644 visionA-frontend/src/stores/conversion-store.ts create mode 100644 visionA-frontend/src/types/conversion.ts diff --git a/visionA-frontend/src/app/conversion/components/ChipSelector.tsx b/visionA-frontend/src/app/conversion/components/ChipSelector.tsx new file mode 100644 index 0000000..99af38d --- /dev/null +++ b/visionA-frontend/src/app/conversion/components/ChipSelector.tsx @@ -0,0 +1,86 @@ +"use client"; + +/** + * ChipSelector — KL520 / KL630 / KL720 / KL730 RadioGroup + * + * Phase 0.8 conversion (見 .autoflow/03-design/wireframes/wireframe-conversion.md §4.1) + * + * 設計目標: + * - 4 個 chip 並排,視覺像「選擇按鈕」(active 是實心、未選是 outline) + * - 鍵盤 ←/→ 切換、Tab 進出、Enter / Space 選中(標準 radiogroup 行為,由原生 radio input 提供) + * + * a11y: + * - 用原生 `` 包 `