feat: finalize portal no-iframe migration baseline and archive change

This commit is contained in:
egg
2026-02-11 13:25:03 +08:00
parent cd54d7cdcb
commit ccab10bee8
117 changed files with 6673 additions and 1098 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-02-11

View File

@@ -0,0 +1,172 @@
## 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 延後到二階段?

View File

@@ -0,0 +1,64 @@
## Why
目前前端處於「多入口頁 + portal iframe 切頁 + 分散 CSS」型態已出現幾個結構性問題頁面生命週期分裂、導覽與內容耦合、樣式規範難以統一、重用元件難以制度化。專案已大量採用 Vite + Vue 3現在具備升級為單一 SPA Shell 的條件,應趁此時移除 iframe 並建立可持續擴充的前端架構基線。
## What Changes
- 建立前端 SPA Shell`Vite + Vue 3` 為單一入口,採 `Vue Router` 管理報表模組切換。
- 完整移除 iframe 架構portal 不再以 `frame_id/toolFrame` 嵌入內容,改為標準路由渲染。
- 導入 Tailwind CSS 作為主樣式系統,建立統一設計 token、元件風格與版面規則逐步取代現有分散 CSS。
- 建立前端動效機制基線:以 `Vue Transition` 為預設,保留 `Motion/GSAP` 擴充通道,用於跨頁過場與重點互動。
- 盤點可重用元件並收斂成共用 UI 層(如 Filter、Table、Card、KPI、Pagination 等),降低重複實作。
- 明確採用 legacy 頁面過渡策略:`job-query``excel-query``query-tool``tmtt-defect` 先以路由包裝整合進新殼層,後續再分批重寫為標準 Vue 模組。
- 保留既有後端 API 與權限邏輯,優先完成前端殼層與導航機制遷移,再進行頁面內部重構。
- **BREAKING**: portal 由「iframe 同頁切頁」改為「SPA 路由切換」,舊有 frame 相關 DOM/測試契約不再成立。
## Capabilities
### New Capabilities
- `spa-shell-navigation`: 建立 Vue Router 為核心的報表導航殼層,取代 iframe 切頁機制。
- `tailwind-design-system`: 以 Tailwind + token + 共用元件規範統一前端樣式。
- `frontend-motion-system`: 定義頁面過場與互動動效的可維護實作策略Vue Transition 為主,進階情境可擴充)。
- `legacy-page-wrapper-strategy`: 定義 legacy 頁面先包裝、後重寫的過渡標準與邊界。
### Modified Capabilities
- `portal-drawer-navigation`: 從 iframe frame-target 導覽改為 router-aware 導覽,維持抽屜分組與權限規則。
- `vue-vite-page-architecture`: 從多獨立頁入口演進到 SPA shell + 路由模組化,並納入 legacy-wrapper 相容模式。
- `full-vite-page-modularization`: 將既有共用邏輯由頁面級搬移至共用模組與設計系統層,提升重用與一致性。
- `migration-gates-and-rollout`: 將本次搬遷的上線/回滾條件明確化為可量測 gate避免「可部署但不可用」風險。
## Impact
- Affected frontend app structure:
- `frontend/src/portal/*`(改為 SPA shell / router host
- `frontend/vite.config.js`(入口與打包策略調整)
- `frontend/src/*` 多頁模組(路由化、共用元件化、樣式收斂)
- Affected templates/routes:
- `src/mes_dashboard/templates/portal.html`iframe 區塊移除)
- Flask route 對 SPA entry 與 fallback 行為需重新定義
- Affected shared styling:
- 現有 `wip-shared/resource-shared` 樣式與各頁 style.css 將分階段合併到 Tailwind 設計系統
- Affected testing:
- 模板整合測試、E2E、壓測需從 iframe 契約改為 router/navigation 契約
- 需新增 route-level smoke、drawer visibility parity、legacy wrapper contract、核心 API parity gate
- Dependency change:
- 新增 Tailwind CSS必要時含 PostCSS 生態)
- 動效方案預設不新增第三方;僅在必要場景引入 GSAP 或等價方案
- Explicit migration decision:
- `job-query``excel-query``query-tool``tmtt-defect` 採「先包裝、後重寫」策略
## Implementation Start Protocol
- 實作啟動時(第一個 `/opsx:apply` session必須先完成 baseline 產出,再進行功能改造:
- drawer visibility baselineadmin / non-admin
- route + query contract baselineP0/P1 頁面)
- critical API payload key/type baseline
- 在 baseline 產出並經 review 確認前,不進行 iframe 拆除與 Router 切換提交。
- 所有切換以 gate 驅動,不以主觀感受判定可上線。
## Release Safety Criteria
- 不可發生 P0 route 無法進入或 core workflow 中斷。
- 抽屜可見性admin / non-admin與 baseline 差異必須為 0。
- 既有 URL/query 行為不可破壞(含 drill-down 與 direct-link
- 必須具備可演練且可在時限內完成的回滾路徑(含 kill-switch

View File

@@ -0,0 +1,24 @@
## ADDED Requirements
### Requirement: Navigation transitions SHALL use a maintainable baseline motion system
The frontend SHALL provide route and panel transition effects using a baseline motion mechanism suitable for long-term maintenance.
#### Scenario: Route transition feedback
- **WHEN** a user navigates between report modules
- **THEN** the shell SHALL provide consistent transition feedback
- **THEN** transitions SHALL NOT block route completion or data loading
### Requirement: Motion behavior SHALL support reduced-motion accessibility
The motion system SHALL respect reduced-motion user preferences.
#### Scenario: Reduced-motion preference
- **WHEN** user agent indicates reduced motion preference
- **THEN** non-essential animations SHALL be minimized or disabled
- **THEN** primary interactions SHALL remain fully usable
### Requirement: Motion effects SHALL preserve functional correctness
Animation implementation SHALL NOT alter data correctness, query timing semantics, or interaction outcomes.
#### Scenario: Interactive action during motion
- **WHEN** users perform filtering, refresh, or drill-down actions during transitions
- **THEN** resulting API calls and state updates SHALL remain functionally equivalent to non-animated execution

View File

@@ -0,0 +1,23 @@
## MODIFIED Requirements
### Requirement: Major Pages SHALL be Managed by Vite Modules
The system SHALL provide Vite-managed module entries for major portal pages under a phased SPA-shell migration while keeping direct route access compatible.
#### Scenario: Portal shell module loading
- **WHEN** the portal experience is rendered
- **THEN** shell behavior MUST load from Vite-built module assets when available
#### Scenario: Module fallback continuity
- **WHEN** a required Vite asset is unavailable in a migration phase
- **THEN** the system MUST keep affected page behavior functional through explicit fallback logic
### Requirement: Modularization MUST Preserve Established Navigation and Drill-Down Semantics
Refactoring into Vite modules and SPA shell routing SHALL not alter existing route paths, query semantics, and drill-down entry points.
#### Scenario: User follows existing drill-down path
- **WHEN** the user navigates from summary page to detail views
- **THEN** the resulting flow and parameter semantics MUST match the established baseline behavior
#### Scenario: Direct detail route remains valid
- **WHEN** users open existing detail routes directly with query parameters (e.g., `/wip-detail?workcenter=...`, `/hold-detail?reason=...`)
- **THEN** route-level behavior MUST remain compatible with established baseline expectations

View File

@@ -0,0 +1,23 @@
## ADDED Requirements
### Requirement: Selected legacy pages SHALL be integrated via wrapper-first strategy
The migration SHALL integrate `job-query`, `excel-query`, `query-tool`, and `tmtt-defect` through wrapper-based routing before full rewrites.
#### Scenario: Wrapper route availability for selected pages
- **WHEN** users navigate to each selected legacy page from the new shell
- **THEN** the route SHALL remain reachable and functionally usable through the wrapper layer
### Requirement: Wrapper mode SHALL preserve legacy functional parity
Wrapper integration SHALL preserve current API interactions, core user workflows, and error handling semantics for wrapped pages.
#### Scenario: Legacy workflow parity under wrapper
- **WHEN** users execute core operations on a wrapped page (query/filter/export where applicable)
- **THEN** operation results SHALL remain behaviorally equivalent to pre-wrapper baseline
### Requirement: Wrapper phase SHALL define rewrite exit criteria
Each wrapped page SHALL have explicit readiness criteria that gate transition from wrapper mode to full Vue module rewrite.
#### Scenario: Rewrite readiness decision
- **WHEN** a wrapped page reaches agreed quality and parity thresholds
- **THEN** the page SHALL be eligible for rewrite scheduling
- **THEN** wrapper decommission SHALL only occur after rewrite parity validation passes

View File

@@ -0,0 +1,23 @@
## MODIFIED Requirements
### Requirement: Migration Gates SHALL Define Cutover Readiness
The system SHALL define explicit migration gates for functional parity, build integrity, drawer visibility parity, and operational health before final cutover.
#### Scenario: Gate evaluation before cutover
- **WHEN** release is prepared for final cutover
- **THEN** all required migration gates MUST pass or cutover SHALL be blocked
#### Scenario: Functional parity gate fails
- **WHEN** any critical route or core workflow parity check fails during gate execution
- **THEN** release governance MUST treat the cutover as failed and prevent promotion
### Requirement: Rollout and Rollback Procedures MUST be Actionable
The system SHALL document actionable rollout and rollback procedures for SPA-shell migration and iframe decommission.
#### Scenario: Rollback execution
- **WHEN** post-cutover validation fails critical checks
- **THEN** operators MUST be able to execute documented rollback steps to restore previous stable behavior
#### Scenario: Kill-switch rollback
- **WHEN** severe production regression is detected after cutover
- **THEN** operators MUST be able to disable the new navigation path through a documented kill-switch mechanism and recover service usability within the defined rollback target time

View File

@@ -0,0 +1,47 @@
## MODIFIED Requirements
### Requirement: Portal Navigation SHALL Group Entries by Functional Drawers
The portal SHALL group navigation entries into functional drawers as defined in the `drawers` configuration of `page_status.json`, rendered by the active portal runtime (server template or SPA shell) without changing drawer assignment semantics.
#### Scenario: Drawer grouping visibility
- **WHEN** users open the portal
- **THEN** the sidebar SHALL display drawers in the order defined by each drawer's `order` field
- **THEN** each drawer SHALL show only the pages assigned to it via `drawer_id`, sorted by each page's `order` field
#### Scenario: Admin-only drawer visibility
- **WHEN** a drawer has `admin_only: true` and the current user is not admin
- **THEN** the drawer and all its pages SHALL NOT be rendered in the sidebar
#### Scenario: Empty drawer visibility
- **WHEN** a drawer has no visible pages (all filtered out by page visibility checks)
- **THEN** the drawer group title SHALL NOT be rendered
### Requirement: Existing Page Behavior SHALL Remain Compatible
The portal navigation refactor SHALL preserve existing target routes while replacing iframe-based page embedding with route-driven navigation.
#### Scenario: Route continuity
- **WHEN** a user selects an existing page entry from a drawer
- **THEN** the corresponding original route SHALL be loaded without changing page business logic behavior
#### Scenario: Direct navigation without iframe
- **WHEN** a sidebar item is clicked
- **THEN** the browser SHALL navigate to the page's route in the same window
- **THEN** the portal SHALL NOT render or activate iframe elements for page content
## ADDED Requirements
### Requirement: Drawer Configuration and Visibility SHALL Remain Deterministic During Migration
Migration to SPA navigation SHALL preserve the effective drawer visibility outcomes defined by current `drawers + pages + status + admin_only` rules.
#### Scenario: Non-admin visible drawer pages remain stable
- **WHEN** a non-admin user opens the portal after migration
- **THEN** only pages with released visibility in non-admin drawers SHALL be visible
- **THEN** admin-only drawers SHALL remain hidden
#### Scenario: Admin visible drawer pages remain stable
- **WHEN** an admin user opens the portal after migration
- **THEN** all pages allowed by drawer assignment and page status rules SHALL remain visible
#### Scenario: Duplicate order values resolve deterministically
- **WHEN** multiple pages or drawers share the same `order` value
- **THEN** rendering order SHALL still be deterministic and repeatable across requests

View File

@@ -0,0 +1,28 @@
## ADDED Requirements
### Requirement: Portal SHALL provide a SPA shell driven by Vue Router
The portal frontend SHALL use a single SPA shell entry and Vue Router to render page modules without iframe embedding.
#### Scenario: Drawer navigation renders router view
- **WHEN** a user clicks a sidebar page entry
- **THEN** the active route SHALL be updated through Vue Router
- **THEN** the main content area SHALL render the corresponding route view without iframe usage
### Requirement: Existing route contracts SHALL remain stable in SPA mode
Migration to SPA shell SHALL preserve existing route paths and deep-link behavior.
#### Scenario: Direct route entry remains functional
- **WHEN** a user opens an existing route directly (bookmark or refresh)
- **THEN** the route SHALL resolve to the same page functionality as before migration
- **THEN** required query parameters SHALL continue to be interpreted with compatible semantics
### Requirement: SPA shell navigation SHALL enforce page visibility rules
SPA navigation SHALL respect backend-defined drawer and page visibility outcomes.
#### Scenario: Non-admin visibility in SPA shell
- **WHEN** a non-admin user opens the shell
- **THEN** routes and drawer items restricted to admin-only visibility SHALL NOT be presented as navigable entries
#### Scenario: Admin visibility in SPA shell
- **WHEN** an admin user opens the shell
- **THEN** pages allowed by drawer and page status rules SHALL be presented as navigable entries

View File

@@ -0,0 +1,25 @@
## ADDED Requirements
### Requirement: Frontend styles SHALL be governed by Tailwind design tokens
The frontend SHALL define a Tailwind-based design token system for color, spacing, typography, radius, and elevation to ensure consistent styling across modules.
#### Scenario: Shared token usage across modules
- **WHEN** two report modules render equivalent UI elements (e.g., card, filter chip, primary button)
- **THEN** they SHALL use the same token-backed style semantics
- **THEN** visual output SHALL remain consistent across modules
### Requirement: Tailwind migration SHALL support coexistence with legacy CSS
The migration SHALL allow Tailwind and existing page CSS to coexist during phased rollout without breaking existing pages.
#### Scenario: Legacy page remains functional during coexistence
- **WHEN** a not-yet-migrated page is rendered
- **THEN** existing CSS behavior SHALL remain intact
- **THEN** Tailwind introduction SHALL NOT cause blocking style regressions
### Requirement: New shared UI components SHALL prefer Tailwind-first styling
Newly introduced shared components SHALL be implemented with Tailwind-first conventions to avoid expanding duplicated page-local CSS.
#### Scenario: Shared component adoption
- **WHEN** a new shared component is introduced in migration scope
- **THEN** its primary style contract SHALL be expressed through Tailwind utilities/components
- **THEN** page-local CSS additions SHALL be minimized and justified

View File

@@ -0,0 +1,19 @@
## MODIFIED Requirements
### Requirement: Pure Vite pages SHALL be served as static HTML
The system SHALL support serving Vite-built HTML pages directly via Flask without Jinja2 rendering.
#### Scenario: Serve pure Vite page
- **WHEN** user navigates to a pure Vite page route (e.g., `/qc-gate`)
- **THEN** Flask SHALL serve the pre-built HTML file from `static/dist/` via `send_from_directory`
- **THEN** the HTML SHALL NOT pass through Jinja2 template rendering
#### Scenario: Page works as top-level navigation target
- **WHEN** a pure Vite page is opened from portal direct navigation
- **THEN** the page SHALL render correctly as a top-level route without iframe embedding dependency
- **THEN** page functionality SHALL NOT rely on portal-managed frame lifecycle
#### Scenario: Direct URL with query parameters remains valid
- **WHEN** users directly open a pure Vite route with existing query parameters (e.g., `/wip-detail?workcenter=...`)
- **THEN** the page SHALL preserve existing parameter semantics and load behavior
- **THEN** SPA shell integration SHALL NOT break direct route entry

View File

@@ -0,0 +1,97 @@
## 0. Implementation Kickoff (Apply Session Day-1)
- [x] 0.1 Generate and commit migration baseline snapshots (drawer visibility, route/query contracts, critical API payload key/type).
- [x] 0.2 Create parity checklist artifacts mapped to the functional parity matrix routes.
- [x] 0.3 Define and verify cutover control mechanism (feature flag / env toggle) before any breaking navigation change.
- [x] 0.4 Record rollback rehearsal plan with target recovery SLO and responsible operator steps.
## 1. Drawer Baseline and Governance Contract
- [x] 1.1 Capture the current production drawer baseline from `data/page_status.json` (id/name/order/admin_only/pages) as migration reference data.
- [x] 1.2 Define canonical drawer responsibilities: IA grouping, ordering, and permission visibility only (no iframe/frame loading semantics).
- [x] 1.3 Define a drawer-route consistency contract (route exists, drawer exists, order is valid, admin_only behavior is deterministic).
- [x] 1.4 Add validation checks/tests for admin and non-admin drawer visibility against the current baseline configuration.
- [x] 1.5 Define `frame_id/tool_src` deprecation policy and transition checkpoints.
## 2. SPA Shell and Router Foundation
- [x] 2.1 Create a SPA shell entry for portal navigation using Vue 3 + Vue Router.
- [x] 2.2 Build router records from drawer/page route contracts while preserving existing URL compatibility.
- [x] 2.3 Implement router-aware sidebar active state and breadcrumb/title metadata handling.
- [x] 2.4 Align auth/permission checks between backend route guard and frontend navigation guard behavior.
- [x] 2.5 Keep health-status widget behavior available in shell without iframe coupling.
## 3. Portal Iframe Decommission
- [x] 3.1 Refactor `portal.html` to remove iframe panel DOM and switch sidebar metadata to route-driven navigation.
- [x] 3.2 Refactor `frontend/src/portal/main.js` to remove frame activation/lazy-load/unload logic.
- [x] 3.3 Replace iframe-specific UI states with route-transition states and loading indicators.
- [x] 3.4 Remove portal CSS rules that target iframe layout while preserving current visual structure.
- [x] 3.5 Verify non-admin/admin navigation outcomes for all current drawers under direct routing.
## 4. Tailwind Design System Bootstrap
- [x] 4.1 Introduce Tailwind CSS + PostCSS configuration into the frontend build pipeline.
- [x] 4.2 Define design tokens (color, spacing, typography, radius, elevation, z-index) mapped to existing UI language.
- [x] 4.3 Establish global base/component/utility layers and migration-safe style ordering.
- [x] 4.4 Define style governance rules to prevent new large page-local CSS during migration.
- [x] 4.5 Publish migration guide for converting existing CSS modules/pages to Tailwind patterns.
## 5. Shared UI and Composable Consolidation
- [x] 5.1 Inventory duplicated UI patterns across WIP/Resource/Hold/QC pages (filter bar, KPI cards, tables, pagination, badges, banners).
- [x] 5.2 Create shared UI component layer and normalize props/events/slot contracts.
- [x] 5.3 Consolidate cross-page composables (auto-refresh, autocomplete, query state, pagination state) under shared modules.
- [x] 5.4 Migrate existing pages to shared components incrementally with visual parity checks.
- [x] 5.5 Remove obsolete duplicated component/style artifacts after each migration batch.
## 6. Legacy Page Wrapper Phase (Confirmed Decision)
- [x] 6.1 Implement wrapper integration for `job-query` inside the new router/shell flow.
- [x] 6.2 Implement wrapper integration for `excel-query` inside the new router/shell flow.
- [x] 6.3 Implement wrapper integration for `query-tool` inside the new router/shell flow.
- [x] 6.4 Implement wrapper integration for `tmtt-defect` inside the new router/shell flow.
- [x] 6.5 Define wrapper-level telemetry (load success, error, latency) and fallback behavior.
- [x] 6.6 Document hard exit criteria that determine when each wrapped page can be considered rewrite-ready.
## 7. Legacy Rewrite Execution (Post-Wrapper)
- Reference checklist: `docs/migration/portal-no-iframe/legacy_rewrite_smoke_checklists.md`
- Reference exemplar: `docs/migration/portal-no-iframe/tmtt_rewrite_exemplar.md`
- Reference playbook: `docs/migration/portal-no-iframe/legacy_rewrite_playbook.md`
- Reference decommission record: `docs/migration/portal-no-iframe/wrapper_decommission_report.md`
- [x] 7.1 Prioritize rewrite order among wrapped pages using usage/complexity/risk scoring.
- [x] 7.2 Rewrite first legacy page as canonical migration exemplar with shared UI + Tailwind.
- [x] 7.3 Rewrite remaining three legacy pages with reusable migration playbook and acceptance criteria.
- [x] 7.4 Decommission wrappers after rewrite completion and parity validation.
## 8. Interaction and Motion System
- [x] 8.1 Define baseline motion guidelines using Vue Transition (route transitions, panel changes, loading states).
- [x] 8.2 Implement reduced-motion accessibility behavior and fallback styles.
- [x] 8.3 Add key interaction transitions for filter apply, chart/table refresh, and drawer navigation.
- [x] 8.4 Define an escalation rule for when GSAP (or equivalent) is allowed beyond baseline transitions.
## 9. Testing, Quality Gates, and Performance
- [x] 9.1 Update unit/template tests from iframe assumptions to router/drawer contract assertions.
- [x] 9.2 Update E2E and stress suites to validate route navigation stability instead of iframe switching.
- [x] 9.3 Add regression tests for drawer ordering, admin_only filtering, and mixed release/dev visibility.
- [x] 9.4 Add contract tests for legacy wrapper routing and fallback behavior.
- [x] 9.5 Establish performance baselines (first paint, route switch latency, memory footprint) and compare pre/post migration.
## 10. Rollout, Cleanup, and Spec Closure
- [x] 10.1 Define phased rollout plan with canary scope and success/error thresholds.
- [x] 10.2 Define rollback strategy for shell/router cutover and wrapper failures.
- [x] 10.3 Remove `frame_id/tool_src` from runtime navigation payload after wrapper-to-rewrite milestones are complete.
- [x] 10.4 Sync changed requirements into main specs and prepare archive criteria for this migration change.
## 11. Cutover Gate Enforcement (Measurable)
- [x] 11.1 Enforce G1 route availability gate: P0 routes return 2xx/3xx at 100% pass rate in release validation.
- [x] 11.2 Enforce G2 drawer parity gate: admin/non-admin visible route sets must match pre-migration baseline exactly (delta = 0).
- [x] 11.3 Enforce G3 workflow parity gate: one critical smoke flow per route in parity matrix must pass at 100%.
- [x] 11.4 Enforce G4 client stability gate: zero unhandled JavaScript runtime errors on critical E2E paths.
- [x] 11.5 Enforce G5 data contract gate: required payload key/type parity checks must pass for all critical APIs.
- [x] 11.6 Enforce G7 rollback readiness gate: rollback rehearsal must recover stable navigation within target SLO (e.g., <= 15 minutes).

View File

@@ -0,0 +1,27 @@
## Purpose
Define stable requirements for frontend-motion-system.
## Requirements
### Requirement: Navigation transitions SHALL use a maintainable baseline motion system
The frontend SHALL provide route and panel transition effects using a baseline motion mechanism suitable for long-term maintenance.
#### Scenario: Route transition feedback
- **WHEN** a user navigates between report modules
- **THEN** the shell SHALL provide consistent transition feedback
- **THEN** transitions SHALL NOT block route completion or data loading
### Requirement: Motion behavior SHALL support reduced-motion accessibility
The motion system SHALL respect reduced-motion user preferences.
#### Scenario: Reduced-motion preference
- **WHEN** user agent indicates reduced motion preference
- **THEN** non-essential animations SHALL be minimized or disabled
- **THEN** primary interactions SHALL remain fully usable
### Requirement: Motion effects SHALL preserve functional correctness
Animation implementation SHALL NOT alter data correctness, query timing semantics, or interaction outcomes.
#### Scenario: Interactive action during motion
- **WHEN** users perform filtering, refresh, or drill-down actions during transitions
- **THEN** resulting API calls and state updates SHALL remain functionally equivalent to non-animated execution

View File

@@ -2,15 +2,15 @@
Define stable requirements for full-vite-page-modularization.
## Requirements
### Requirement: Major Pages SHALL be Managed by Vite Modules
The system SHALL provide Vite-managed module entries for major portal pages, replacing inline scripts in a phased manner.
The system SHALL provide Vite-managed module entries for major portal pages under a phased SPA-shell migration while keeping direct route access compatible.
#### Scenario: Portal module loading
- **WHEN** the portal page is rendered
#### Scenario: Portal shell module loading
- **WHEN** the portal experience is rendered
- **THEN** it MUST load its behavior from a Vite-built module asset when available
#### Scenario: Page module fallback
#### Scenario: Module fallback continuity
- **WHEN** a required Vite asset is unavailable
- **THEN** the system MUST keep page behavior functional through explicit fallback logic
- **THEN** the system MUST keep affected page behavior functional through explicit fallback logic
### Requirement: Build Pipeline SHALL Produce Backend-Served Assets
Vite build output MUST be emitted into backend static paths and served by Flask/Gunicorn on the same origin.
@@ -27,12 +27,16 @@ Page entry modules MUST consume shared chart/query/drawer utilities for common b
- **THEN** the behavior MUST be provided by shared Vite modules rather than duplicated page-local implementations
### Requirement: Modularization MUST Preserve Established Navigation and Drill-Down Semantics
Refactoring into Vite modules SHALL not alter existing page transitions, independent tabs, and drill-down entry points.
Refactoring into Vite modules and SPA shell routing SHALL not alter existing route paths, query semantics, and drill-down entry points.
#### Scenario: User follows existing drill-down path
- **WHEN** the user navigates from summary page to detail views
- **THEN** the resulting flow and parameter semantics MUST match the established baseline behavior
#### Scenario: Direct detail route remains valid
- **WHEN** users open existing detail routes directly with query parameters (e.g., `/wip-detail?workcenter=...`, `/hold-detail?reason=...`)
- **THEN** route-level behavior MUST remain compatible with established baseline expectations
### Requirement: Module Boundaries SHALL Support Frontend Compute Expansion
Vite module structure MUST keep compute logic decoupled from DOM wiring so additional backend-to-frontend computation shifts can be added safely.

View File

@@ -0,0 +1,26 @@
## Purpose
Define stable requirements for legacy-page-wrapper-strategy.
## Requirements
### Requirement: Selected legacy pages SHALL be integrated via wrapper-first strategy
The migration SHALL integrate `job-query`, `excel-query`, `query-tool`, and `tmtt-defect` through wrapper-based routing before full rewrites.
#### Scenario: Wrapper route availability for selected pages
- **WHEN** users navigate to each selected legacy page from the new shell
- **THEN** the route SHALL remain reachable and functionally usable through the wrapper layer
### Requirement: Wrapper mode SHALL preserve legacy functional parity
Wrapper integration SHALL preserve current API interactions, core user workflows, and error handling semantics for wrapped pages.
#### Scenario: Legacy workflow parity under wrapper
- **WHEN** users execute core operations on a wrapped page (query/filter/export where applicable)
- **THEN** operation results SHALL remain behaviorally equivalent to pre-wrapper baseline
### Requirement: Wrapper phase SHALL define rewrite exit criteria
Each wrapped page SHALL have explicit readiness criteria that gate transition from wrapper mode to full Vue module rewrite.
#### Scenario: Rewrite readiness decision
- **WHEN** a wrapped page reaches agreed quality and parity thresholds
- **THEN** the page SHALL be eligible for rewrite scheduling
- **THEN** wrapper decommission SHALL only occur after rewrite parity validation passes

View File

@@ -2,19 +2,27 @@
Define stable requirements for migration-gates-and-rollout.
## Requirements
### Requirement: Migration Gates SHALL Define Cutover Readiness
The system SHALL define explicit migration gates for functional parity, build integrity, and operational health before final cutover.
The system SHALL define explicit migration gates for functional parity, build integrity, drawer visibility parity, and operational health before final cutover.
#### Scenario: Gate evaluation before cutover
- **WHEN** release is prepared for final cutover
- **THEN** all required migration gates MUST pass or cutover SHALL be blocked
#### Scenario: Functional parity gate fails
- **WHEN** any critical route or core workflow parity check fails during gate execution
- **THEN** release governance MUST treat the cutover as failed and prevent promotion
### Requirement: Rollout and Rollback Procedures MUST be Actionable
The system SHALL document actionable rollout and rollback procedures for root migration.
The system SHALL document actionable rollout and rollback procedures for SPA-shell migration and iframe decommission.
#### Scenario: Rollback execution
- **WHEN** post-cutover validation fails critical checks
- **THEN** operators MUST be able to execute documented rollback steps to restore previous stable behavior
#### Scenario: Kill-switch rollback
- **WHEN** severe production regression is detected after cutover
- **THEN** operators MUST be able to disable the new navigation path through a documented kill-switch mechanism and recover service usability within the defined rollback target time
### Requirement: Migration Gates SHALL Include Runtime Resilience Validation
Cutover readiness gates MUST include resilience checks for pool exhaustion handling, circuit-breaker fail-fast behavior, and recovery flow.

View File

@@ -3,9 +3,8 @@ Define stable requirements for portal-drawer-navigation.
## Requirements
### Requirement: Portal Navigation SHALL Group Entries by Functional Drawers
The portal SHALL group navigation entries into functional drawers as defined in the `drawers` configuration of `page_status.json`, rendered dynamically via Jinja2 loop instead of hardcoded HTML.
The portal SHALL group navigation entries into functional drawers as defined in the `drawers` configuration of `page_status.json`, rendered by the active portal runtime (server template or SPA shell) without changing drawer assignment semantics.
#### Scenario: Drawer grouping visibility
- **WHEN** users open the portal
@@ -17,29 +16,33 @@ The portal SHALL group navigation entries into functional drawers as defined in
- **THEN** the drawer and all its pages SHALL NOT be rendered in the sidebar
#### Scenario: Empty drawer visibility
- **WHEN** a drawer has no visible pages (all filtered out by `can_view_page()`)
- **WHEN** a drawer has no visible pages (all filtered out by page visibility checks)
- **THEN** the drawer group title SHALL NOT be rendered
### Requirement: Existing Page Behavior SHALL Remain Compatible
The portal navigation refactor SHALL preserve existing target routes and lazy-load behavior for content frames.
The portal navigation refactor SHALL preserve existing target routes while replacing iframe-based page embedding with route-driven navigation.
#### Scenario: Route continuity
- **WHEN** a user selects an existing page entry from a dynamically rendered drawer
- **WHEN** a user selects an existing page entry from a drawer
- **THEN** the corresponding original route SHALL be loaded without changing page business logic behavior
#### Scenario: Iframe lazy-load continuity
- **WHEN** a sidebar item is clicked for the first time
- **THEN** the iframe SHALL lazy-load its content from the page's route, consistent with current behavior
#### Scenario: Direct navigation without iframe
- **WHEN** a sidebar item is clicked
- **THEN** the browser SHALL navigate to the page's route in the same window
- **THEN** the portal SHALL NOT render or activate iframe elements for page content
### Requirement: First-run migration SHALL populate drawer configuration automatically
When `page_status.json` does not contain a `drawers` field, the system SHALL automatically create the default drawer structure matching the current hardcoded layout and assign existing pages to their corresponding drawers.
### Requirement: Drawer Configuration and Visibility SHALL Remain Deterministic During Migration
Migration to SPA navigation SHALL preserve the effective drawer visibility outcomes defined by current `drawers + pages + status + admin_only` rules.
#### Scenario: First startup after deployment
- **WHEN** the application starts and `page_status.json` has no `drawers` field
- **THEN** the system SHALL create three default drawers (報表類, 查詢類, 開發工具)
- **THEN** the system SHALL assign each existing page to its historically correct drawer
- **THEN** the system SHALL persist the updated configuration immediately
#### Scenario: Non-admin visible drawer pages remain stable
- **WHEN** a non-admin user opens the portal after migration
- **THEN** only pages with released visibility in non-admin drawers SHALL be visible
- **THEN** admin-only drawers SHALL remain hidden
#### Scenario: Subsequent startup
- **WHEN** the application starts and `page_status.json` already contains a `drawers` field
- **THEN** the system SHALL NOT modify the existing drawer configuration
#### Scenario: Admin visible drawer pages remain stable
- **WHEN** an admin user opens the portal after migration
- **THEN** all pages allowed by drawer assignment and page status rules SHALL remain visible
#### Scenario: Duplicate order values resolve deterministically
- **WHEN** multiple pages or drawers share the same `order` value
- **THEN** rendering order SHALL still be deterministic and repeatable across requests

View File

@@ -0,0 +1,31 @@
## Purpose
Define stable requirements for spa-shell-navigation.
## Requirements
### Requirement: Portal SHALL provide a SPA shell driven by Vue Router
The portal frontend SHALL use a single SPA shell entry and Vue Router to render page modules without iframe embedding.
#### Scenario: Drawer navigation renders router view
- **WHEN** a user clicks a sidebar page entry
- **THEN** the active route SHALL be updated through Vue Router
- **THEN** the main content area SHALL render the corresponding route view without iframe usage
### Requirement: Existing route contracts SHALL remain stable in SPA mode
Migration to SPA shell SHALL preserve existing route paths and deep-link behavior.
#### Scenario: Direct route entry remains functional
- **WHEN** a user opens an existing route directly (bookmark or refresh)
- **THEN** the route SHALL resolve to the same page functionality as before migration
- **THEN** required query parameters SHALL continue to be interpreted with compatible semantics
### Requirement: SPA shell navigation SHALL enforce page visibility rules
SPA navigation SHALL respect backend-defined drawer and page visibility outcomes.
#### Scenario: Non-admin visibility in SPA shell
- **WHEN** a non-admin user opens the shell
- **THEN** routes and drawer items restricted to admin-only visibility SHALL NOT be presented as navigable entries
#### Scenario: Admin visibility in SPA shell
- **WHEN** an admin user opens the shell
- **THEN** pages allowed by drawer and page status rules SHALL be presented as navigable entries

View File

@@ -0,0 +1,28 @@
## Purpose
Define stable requirements for tailwind-design-system.
## Requirements
### Requirement: Frontend styles SHALL be governed by Tailwind design tokens
The frontend SHALL define a Tailwind-based design token system for color, spacing, typography, radius, and elevation to ensure consistent styling across modules.
#### Scenario: Shared token usage across modules
- **WHEN** two report modules render equivalent UI elements (e.g., card, filter chip, primary button)
- **THEN** they SHALL use the same token-backed style semantics
- **THEN** visual output SHALL remain consistent across modules
### Requirement: Tailwind migration SHALL support coexistence with legacy CSS
The migration SHALL allow Tailwind and existing page CSS to coexist during phased rollout without breaking existing pages.
#### Scenario: Legacy page remains functional during coexistence
- **WHEN** a not-yet-migrated page is rendered
- **THEN** existing CSS behavior SHALL remain intact
- **THEN** Tailwind introduction SHALL NOT cause blocking style regressions
### Requirement: New shared UI components SHALL prefer Tailwind-first styling
Newly introduced shared components SHALL be implemented with Tailwind-first conventions to avoid expanding duplicated page-local CSS.
#### Scenario: Shared component adoption
- **WHEN** a new shared component is introduced in migration scope
- **THEN** its primary style contract SHALL be expressed through Tailwind utilities/components
- **THEN** page-local CSS additions SHALL be minimized and justified

View File

@@ -12,10 +12,10 @@ The system SHALL support serving Vite-built HTML pages directly via Flask withou
- **THEN** Flask SHALL serve the pre-built HTML file from `static/dist/` via `send_from_directory`
- **THEN** the HTML SHALL NOT pass through Jinja2 template rendering
#### Scenario: Page works in portal iframe
- **WHEN** the pure Vite page is loaded inside the portal iframe
- **THEN** the page SHALL render correctly within the iframe context
- **THEN** CSP `frame-ancestors 'self'` SHALL allow the embedding
#### Scenario: Page works as top-level navigation target
- **WHEN** a pure Vite page is opened from portal direct navigation
- **THEN** the page SHALL render correctly as a top-level route without iframe embedding dependency
- **THEN** page functionality SHALL NOT rely on portal-managed frame lifecycle
### Requirement: Vite config SHALL support Vue SFC and HTML entry points
The Vite build configuration SHALL support Vue Single File Components alongside existing vanilla JS entries.