Files
DashBoard/openspec/changes/archive/2026-02-11-portal-no-iframe-navigation/design.md

173 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Context
目前 `portal.html` 透過 `iframe + frame_id + toolFrame` 在同一頁面切換多個報表。此模式雖可避免整頁跳轉,但帶來以下問題:
- 內容生命週期拆成多個 frame除錯與事件追蹤困難
- 導覽邏輯被 iframe lazy-load、高度同步、active frame 狀態綁死
- 測試對 DOM/互動契約依賴 iframe 結構,變更成本高
- 已完成 Vite 模組化的頁面其實已可獨立路由載入,不需要 iframe 承載
此外,`drawers` 設定在目前環境已不是初始預設值,而是營運中配置(來源:`data/page_status.json`
- `reports`即時報表order=1admin_only=false
- `drawer-2`歷史報表order=2admin_only=false
- `drawer`查詢工具order=3admin_only=false
- `dev-tools`開發工具order=4admin_only=true
對應頁面已分散配置在上述抽屜,例如:
- 即時報表:`/wip-overview``/hold-overview``/resource``/qc-gate`
- 歷史報表:`/hold-history``/resource-history`
- 查詢工具:`/job-query`
- 開發工具admin pages 與部分工具頁(含 `tables``excel-query``query-tool``tmtt-defect``mid-section-defect`
因此本次改造不只是移除 iframe而是要把「抽屜資訊架構」從「載入技術frame耦合」解耦到「路由與權限治理」。
## Goals / Non-Goals
**Goals:**
- 移除 portal 內容區的 iframe 依賴與 frame 管理邏輯
- 保留抽屜分組、admin 權限過濾、健康狀態檢查 UI
- 將側欄點擊行為改為同視窗路由導頁
- 維持既有 route path 與頁面業務邏輯不變
- 更新測試使其驗證新契約link/navigation
- 在不破壞既有 `drawers/pages` 資料模型下,支持 Router-based 導覽
**Non-Goals:**
- 不在第一階段一次重寫全部 legacy 頁面內容
- 不調整後端 API 介面或權限模型
- 不更動 page_status.json 的資料模型(僅調整 portal 消費方式)
## Decisions
### Decision 1: 抽屜保留後端治理,前端改為 Router-aware 導覽
- **選擇**: `get_navigation_config()` 仍作為抽屜與頁面來源,前端側欄只消費 route / status / admin_only不再消費 frame_id/toolFrame。
- **理由**:
- 保留既有營運中的抽屜設定與管理流程
- 抽屜責任回到 IA分類、排序、權限避免綁定載入技術
- 降低一次性資料遷移與管理頁調整風險
- **備選方案**:
- 改由前端硬編抽屜:短期可行,但與管理後台脫鉤,營運成本增加
### Decision 2: 導入 SPA Shell + Vue Router分階段替換多入口模式
- **選擇**: 建立 SPA shell 承接抽屜導覽,主要報表頁優先轉為 router view既有可獨立頁先保持可直接訪問。
- **理由**:
- 符合大型遷移所需的漸進路線
- 可保留既有 URL 合約並分批改造
- **備選方案**:
- 一次切成全 SPA改動面過大回歸與 rollback 風險高
### Decision 3: Legacy 頁面採先包裝、後重寫策略(已確認)
- **選擇**: `job-query``excel-query``query-tool``tmtt-defect` 先以 wrapper route 納入新殼層,再逐頁重寫為標準 Vue 模組。
- **理由**:
- 先完成抽屜/導航/樣式治理,不被單頁重寫阻塞
- 可逐步替換,控制每次上線風險
- **備選方案**:
- 直接重寫四頁:會延後主幹遷移,且依賴資料邏輯盤點完整度
### Decision 4: Tailwind 為主樣式系統,保留過渡期雙軌
- **選擇**: 新增 Tailwind 設計 token 與元件規範,新功能優先用 Tailwind舊頁 CSS 分批遷移。
- **理由**:
- 先建立統一規範,避免繼續累積散落 CSS
- 遷移節奏可與功能迭代對齊
## Risks / Trade-offs
- **[Risk] 切頁不再常駐多頁狀態,使用者感知切換較慢** → **Mitigation**: 保持頁面 bundle 切分與快取策略,後續再評估 prefetch。
- **[Risk] 既有測試仍假設 iframe DOM 結構** → **Mitigation**: 分階段更新 template/e2e/stress 斷言為 router/navigation 契約。
- **[Risk] 抽屜配置與路由表可能出現不一致** → **Mitigation**: 新增導航一致性檢查(缺失 route、權限錯置、排序衝突
- **[Risk] Tailwind 與既有 CSS 共存期造成樣式衝突** → **Mitigation**: 設定 migration lint 規則與 page-level ownership限制新增散落 CSS。
- **[Risk] Legacy wrapper 週期拉長導致技術債滯留** → **Mitigation**: 在 tasks 中明確列出逐頁重寫里程碑與退出條件。
## Migration Plan
1. 定義抽屜-路由契約(來源、排序、權限、可見性)並建立檢查機制。
2. 建立 SPA shell 與 Router先接管 portal 導覽與主要報表頁入口。
3. 移除 iframe 導覽路徑,保留舊 URL 行為與 fallback。
4. 導入 Tailwind 設計系統並建立共用元件層。
5. 將四個 legacy 頁面先包裝接入新殼層,再分批重寫。
6. 完成測試與觀測遷移模板、E2E、壓測、性能基線
## Current Baseline Snapshot (2026-02-11)
### Effective drawer visibility (derived from current `drawers + pages + status + admin_only`)
- Non-admin visible routes:
- `reports`: `/wip-overview`, `/resource`, `/qc-gate`
- `drawer-2`: `/resource-history`
- `drawer`: `/job-query`
- Admin visible routes:
- `reports`: `/wip-overview`, `/hold-overview`, `/resource`, `/qc-gate`
- `drawer-2`: `/hold-history`, `/resource-history`
- `drawer`: `/job-query`
- `dev-tools`: `/tables`, `/admin/pages`, `/excel-query`, `/admin/performance`, `/query-tool`, `/tmtt-defect`, `/mid-section-defect`
### Query/route contracts that must not regress
- `/wip-overview`: query filters `workorder`, `lotid`, `package`, `type`, `status`
- `/wip-detail`: query filters `workcenter`, `workorder`, `lotid`, `package`, `type`, `status`
- `/hold-detail`: required query `reason` (missing reason redirects away by current server/client guard)
- `/resource-history`: query params built from date range, granularity, groups/families/machines, production flags
## Functional Parity Matrix
| Route / Surface | Migration Mode | Must Preserve |
| --- | --- | --- |
| `/` portal shell | SPA (router host) | Drawer grouping/order/visibility, health widget, auth-linked visibility |
| `/wip-overview` | Vue route view | Filter URL sync, status filter behavior, drill-down to detail pages |
| `/wip-detail` | Vue route view | Query-param entry, pagination/filter semantics, back-link query continuity |
| `/hold-overview` | Vue route view | Hold type/reason filter behavior, treemap/matrix interaction |
| `/hold-history` | Vue route view | Date/record type filter semantics, reason pareto interactions |
| `/resource` | Vue route view | Group/status filtering and summary parity |
| `/resource-history` | Vue route view | Query validation, summary/detail/export behavior parity |
| `/qc-gate` | Vue route view | Chart↔table linked filtering and refresh behaviors |
| `/job-query` | Wrapper first | Resource/date query, transaction query, CSV export |
| `/excel-query` | Wrapper first | Upload/column detect/query/export workflow |
| `/query-tool` | Wrapper first | Resolve/history/association/equipment-period workflow |
| `/tmtt-defect` | Wrapper first | Date-range query and CSV export workflow |
## Data Contract Safety Net
- 建立「遷移前基線快照」:
- drawer visibility snapshotadmin / non-admin
- route response smoke snapshotHTTP status + critical payload keys
- critical page JSON schema snapshotssummary/detail/pagination key sets
- 建立「遷移後對等檢查」:
- key presence parity不可缺欄位
- type compatibility數值/字串/陣列型別)
- empty-state semantics空資料行為一致
- 對 legacy wrapper 頁面增加 wrapper-contract 測試:
- route reachable
- primary query path success
- export path reachable (where applicable)
## Go / No-Go Gates (Cutover)
- G1 Route availability:
- P0 路由portal + major report routes100% 回應 2xx/3xx
- G2 Drawer parity:
- admin/non-admin 可見路由集合與 baseline 差異為 0
- G3 Workflow parity:
- parity matrix 中每頁核心流程至少 1 條 smoke path 通過率 100%
- G4 Client stability:
- E2E 測試中未捕獲未處理 JS runtime errorcritical path
- G5 Data contract:
- critical API payload key/type parity gate 全部通過
- G6 Performance:
- route switch latency 與 baseline 比較不得惡化超過既定閾值
- G7 Rollback readiness:
- rollback rehearsal 完成且時間達標(可在目標時間內恢復舊路徑)
## Rollback / Kill-Switch Strategy
1. 保留舊入口路徑與必要 fallback直到全量 gate 通過。
2. 使用可配置切換feature flag / env-based toggle控制新 shell 導航啟用。
3. 一旦觸發回滾條件G1/G2/G3 任一 critical fail立即切回舊導航路徑。
4. 回滾後保留觀測資料並建立失敗歸因報告,再進行下一輪修復與 rehearsal。
## Open Questions
- `frame_id/tool_src` 欄位何時在資料模型層正式退場?
- legacy wrapper 對使用者是否顯示遷移標記(例如 beta badge
- 動效方案是否在第一版限定 `Vue Transition`GSAP 延後到二階段?