feat: finalize portal no-iframe migration baseline and archive change
This commit is contained in:
33
docs/migration/portal-no-iframe/archive_readiness.md
Normal file
33
docs/migration/portal-no-iframe/archive_readiness.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# OpenSpec Archive Readiness (`portal-no-iframe-navigation`)
|
||||
|
||||
## Spec Sync Scope
|
||||
|
||||
Main specs synchronized/updated for this change:
|
||||
|
||||
- `openspec/specs/full-vite-page-modularization/spec.md`
|
||||
- `openspec/specs/portal-drawer-navigation/spec.md`
|
||||
- `openspec/specs/vue-vite-page-architecture/spec.md`
|
||||
- `openspec/specs/migration-gates-and-rollout/spec.md`
|
||||
- `openspec/specs/spa-shell-navigation/spec.md` (new)
|
||||
- `openspec/specs/tailwind-design-system/spec.md` (new)
|
||||
- `openspec/specs/frontend-motion-system/spec.md` (new)
|
||||
- `openspec/specs/legacy-page-wrapper-strategy/spec.md` (new)
|
||||
|
||||
## Migration Closure Artifacts
|
||||
|
||||
- Rewrite smoke checklist:
|
||||
- `docs/migration/portal-no-iframe/legacy_rewrite_smoke_checklists.md`
|
||||
- Rewrite exemplar:
|
||||
- `docs/migration/portal-no-iframe/tmtt_rewrite_exemplar.md`
|
||||
- Rewrite playbook:
|
||||
- `docs/migration/portal-no-iframe/legacy_rewrite_playbook.md`
|
||||
- Wrapper decommission record:
|
||||
- `docs/migration/portal-no-iframe/wrapper_decommission_report.md`
|
||||
- Frame field retirement record:
|
||||
- `docs/migration/portal-no-iframe/frame_id_tool_src_deprecation_plan.md`
|
||||
|
||||
## Pre-Archive Checklist
|
||||
|
||||
- [x] `openspec validate portal-no-iframe-navigation --strict` passes.
|
||||
- [x] Build and core migration tests pass on latest branch.
|
||||
- [x] Task list in `openspec/changes/portal-no-iframe-navigation/tasks.md` is fully checked.
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"source": "current frontend API consumption contracts",
|
||||
"apis": {
|
||||
"/api/wip/overview/summary": {
|
||||
"required_keys": [
|
||||
"dataUpdateDate",
|
||||
"runLots",
|
||||
"queueLots",
|
||||
"holdLots"
|
||||
],
|
||||
"notes": "summary header and cards depend on these fields"
|
||||
},
|
||||
"/api/wip/overview/matrix": {
|
||||
"required_keys": [
|
||||
"workcenters",
|
||||
"packages",
|
||||
"matrix",
|
||||
"workcenter_totals"
|
||||
],
|
||||
"notes": "matrix table rendering contract"
|
||||
},
|
||||
"/api/wip/hold-detail/summary": {
|
||||
"required_keys": [
|
||||
"workcenterCount",
|
||||
"packageCount",
|
||||
"lotCount"
|
||||
],
|
||||
"notes": "hold detail summary cards contract"
|
||||
},
|
||||
"/api/resource/history/summary": {
|
||||
"required_keys": [
|
||||
"kpi",
|
||||
"trend",
|
||||
"heatmap",
|
||||
"workcenter_comparison"
|
||||
],
|
||||
"notes": "resource history chart summary contract"
|
||||
},
|
||||
"/api/resource/history/detail": {
|
||||
"required_keys": [
|
||||
"data"
|
||||
],
|
||||
"notes": "detail table contract (plus truncated/max_records metadata when present)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"source": "data/page_status.json",
|
||||
"errors": []
|
||||
}
|
||||
177
docs/migration/portal-no-iframe/baseline_drawer_visibility.json
Normal file
177
docs/migration/portal-no-iframe/baseline_drawer_visibility.json
Normal file
@@ -0,0 +1,177 @@
|
||||
{
|
||||
"source": "data/page_status.json",
|
||||
"admin": [
|
||||
{
|
||||
"id": "reports",
|
||||
"name": "即時報表",
|
||||
"order": 1,
|
||||
"admin_only": false,
|
||||
"pages": [
|
||||
{
|
||||
"route": "/wip-overview",
|
||||
"name": "WIP 即時概況",
|
||||
"status": "released",
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"route": "/hold-overview",
|
||||
"name": "Hold 即時概況",
|
||||
"status": "dev",
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"route": "/resource",
|
||||
"name": "設備即時概況",
|
||||
"status": "released",
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"route": "/qc-gate",
|
||||
"name": "QC-GATE 狀態",
|
||||
"status": "released",
|
||||
"order": 6
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "drawer-2",
|
||||
"name": "歷史報表",
|
||||
"order": 2,
|
||||
"admin_only": false,
|
||||
"pages": [
|
||||
{
|
||||
"route": "/hold-history",
|
||||
"name": "Hold 歷史績效",
|
||||
"status": "dev",
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"route": "/resource-history",
|
||||
"name": "設備歷史績效",
|
||||
"status": "released",
|
||||
"order": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "drawer",
|
||||
"name": "查詢工具",
|
||||
"order": 3,
|
||||
"admin_only": false,
|
||||
"pages": [
|
||||
{
|
||||
"route": "/job-query",
|
||||
"name": "設備維修查詢",
|
||||
"status": "released",
|
||||
"order": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dev-tools",
|
||||
"name": "開發工具",
|
||||
"order": 4,
|
||||
"admin_only": true,
|
||||
"pages": [
|
||||
{
|
||||
"route": "/tables",
|
||||
"name": "表格總覽",
|
||||
"status": "dev",
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"route": "/admin/pages",
|
||||
"name": "頁面管理",
|
||||
"status": "dev",
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"route": "/excel-query",
|
||||
"name": "Excel 批次查詢",
|
||||
"status": "dev",
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"route": "/admin/performance",
|
||||
"name": "效能監控",
|
||||
"status": "dev",
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"route": "/query-tool",
|
||||
"name": "批次追蹤工具",
|
||||
"status": "dev",
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"route": "/tmtt-defect",
|
||||
"name": "TMTT印字腳型不良分析",
|
||||
"status": "released",
|
||||
"order": 5
|
||||
},
|
||||
{
|
||||
"route": "/mid-section-defect",
|
||||
"name": "中段製程不良追溯",
|
||||
"status": "dev",
|
||||
"order": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"non_admin": [
|
||||
{
|
||||
"id": "reports",
|
||||
"name": "即時報表",
|
||||
"order": 1,
|
||||
"admin_only": false,
|
||||
"pages": [
|
||||
{
|
||||
"route": "/wip-overview",
|
||||
"name": "WIP 即時概況",
|
||||
"status": "released",
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"route": "/resource",
|
||||
"name": "設備即時概況",
|
||||
"status": "released",
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"route": "/qc-gate",
|
||||
"name": "QC-GATE 狀態",
|
||||
"status": "released",
|
||||
"order": 6
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "drawer-2",
|
||||
"name": "歷史報表",
|
||||
"order": 2,
|
||||
"admin_only": false,
|
||||
"pages": [
|
||||
{
|
||||
"route": "/resource-history",
|
||||
"name": "設備歷史績效",
|
||||
"status": "released",
|
||||
"order": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "drawer",
|
||||
"name": "查詢工具",
|
||||
"order": 3,
|
||||
"admin_only": false,
|
||||
"pages": [
|
||||
{
|
||||
"route": "/job-query",
|
||||
"name": "設備維修查詢",
|
||||
"status": "released",
|
||||
"order": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"source": "frontend route parsing and current parity matrix",
|
||||
"routes": {
|
||||
"/wip-overview": {
|
||||
"query_keys": [
|
||||
"workorder",
|
||||
"lotid",
|
||||
"package",
|
||||
"type",
|
||||
"status"
|
||||
],
|
||||
"notes": "filters + status URL state must remain compatible"
|
||||
},
|
||||
"/wip-detail": {
|
||||
"query_keys": [
|
||||
"workcenter",
|
||||
"workorder",
|
||||
"lotid",
|
||||
"package",
|
||||
"type",
|
||||
"status"
|
||||
],
|
||||
"notes": "workcenter deep-link and back-link query continuity"
|
||||
},
|
||||
"/hold-detail": {
|
||||
"query_keys": [
|
||||
"reason"
|
||||
],
|
||||
"notes": "reason required for normal access flow"
|
||||
},
|
||||
"/resource-history": {
|
||||
"query_keys": [
|
||||
"start_date",
|
||||
"end_date",
|
||||
"granularity",
|
||||
"workcenter_groups",
|
||||
"families",
|
||||
"resource_ids",
|
||||
"is_production",
|
||||
"is_key",
|
||||
"is_monitor"
|
||||
],
|
||||
"notes": "query/export params must remain compatible"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# Drawer Governance Contract (Portal No-Iframe Migration)
|
||||
|
||||
## Scope
|
||||
|
||||
This contract defines drawer behavior that must remain stable during migration.
|
||||
|
||||
## Canonical Responsibilities
|
||||
|
||||
Drawer metadata is responsible for:
|
||||
|
||||
- Information architecture grouping.
|
||||
- Display order.
|
||||
- Access visibility (`admin_only`).
|
||||
|
||||
Drawer metadata is not responsible for:
|
||||
|
||||
- Content embedding mode (`iframe`, `toolFrame`).
|
||||
- Rendering technology selection (Jinja vs SPA route view).
|
||||
|
||||
## Contract Rules
|
||||
|
||||
1. Drawer IDs must be unique and non-empty.
|
||||
2. Page routes must be unique and non-empty.
|
||||
3. `page.drawer_id` (when present) must reference an existing drawer.
|
||||
4. `order` values (when present) must be positive integers.
|
||||
5. Page status must be one of `released` or `dev`.
|
||||
6. Visibility outcomes must be deterministic for admin/non-admin users.
|
||||
|
||||
## Deterministic Rendering Order
|
||||
|
||||
Drawers:
|
||||
|
||||
- Primary sort by `order` ascending.
|
||||
- Secondary sort by `name` ascending.
|
||||
|
||||
Pages in each drawer:
|
||||
|
||||
- Primary sort by `order` ascending.
|
||||
- Secondary sort by `(name or route)` ascending.
|
||||
|
||||
## Visibility Semantics
|
||||
|
||||
- Non-admin users can view only `released` pages in non-admin-only drawers.
|
||||
- Admin users can view all drawer-assigned pages according to current page status policy.
|
||||
- Drawers with zero visible pages are hidden.
|
||||
|
||||
## Validation Artifacts
|
||||
|
||||
- `baseline_drawer_contract_validation.json`
|
||||
- `baseline_drawer_visibility.json`
|
||||
@@ -0,0 +1,44 @@
|
||||
# `frame_id` / `tool_src` Deprecation Plan
|
||||
|
||||
## Status
|
||||
|
||||
- Retirement completed in this change.
|
||||
- Runtime navigation payload generation no longer emits:
|
||||
- `frame_id`
|
||||
- `tool_src`
|
||||
|
||||
## Context
|
||||
|
||||
Frame-era fields were used for iframe loading compatibility:
|
||||
|
||||
- `frame_id`
|
||||
- `tool_src`
|
||||
|
||||
## Policy
|
||||
|
||||
Deprecation is phased and must not break active routes.
|
||||
|
||||
## Phases
|
||||
|
||||
1. **Compatibility phase**:
|
||||
- Keep fields in payload.
|
||||
- Ensure new router navigation logic does not rely on these fields.
|
||||
2. **Dual-run phase**:
|
||||
- Validate all navigation paths without frame fields.
|
||||
3. **Retirement readiness**:
|
||||
- Wrapper-first pages are stable in shell.
|
||||
- Cutover gates G1~G7 are green in rehearsal.
|
||||
4. **Removal phase**:
|
||||
- Remove generation and downstream usage of `frame_id/tool_src`.
|
||||
- Update related tests and docs.
|
||||
|
||||
## Removal Checkpoints
|
||||
|
||||
- Checkpoint A: drawer parity stable in canary.
|
||||
- Checkpoint B: legacy wrappers stable with no frame-field dependency.
|
||||
- Checkpoint C: rollback mechanism verified independent of frame fields. ✓
|
||||
|
||||
## Risk Controls
|
||||
|
||||
- Keep rollback-safe path via route-level navigation and kill-switch.
|
||||
- Keep gate coverage for route/drawer/workflow parity after field removal.
|
||||
40
docs/migration/portal-no-iframe/legacy_rewrite_playbook.md
Normal file
40
docs/migration/portal-no-iframe/legacy_rewrite_playbook.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Legacy Rewrite Playbook (Batch-2)
|
||||
|
||||
## Target Pages
|
||||
|
||||
This playbook governs rewrite execution for the remaining three legacy pages:
|
||||
|
||||
- `/job-query`
|
||||
- `/excel-query`
|
||||
- `/query-tool`
|
||||
|
||||
Rewrite order follows `legacy_rewrite_priority_matrix.md`.
|
||||
|
||||
## Canonical Steps
|
||||
|
||||
1. Preserve route and API contracts first.
|
||||
2. Move page state and API calls into composables (`use<Page>Data`).
|
||||
3. Replace page-local repeated blocks with shared UI components where possible.
|
||||
4. Keep Tailwind token alignment for new/changed UI.
|
||||
5. Validate with per-page smoke checklist before/after switch.
|
||||
|
||||
## Required Acceptance (Per Page)
|
||||
|
||||
- Route reachable and functional without shell wrapper.
|
||||
- Core query workflow succeeds and returns expected result sections.
|
||||
- Export path remains usable (where applicable).
|
||||
- No new unhandled runtime error on the primary path.
|
||||
- Checklist IDs pass:
|
||||
- `/job-query`: `JOB-SMOKE-01`~`JOB-SMOKE-06`
|
||||
- `/excel-query`: `EXCEL-SMOKE-01`~`EXCEL-SMOKE-06`
|
||||
- `/query-tool`: `QTOOL-SMOKE-01`~`QTOOL-SMOKE-06`
|
||||
|
||||
Checklist source:
|
||||
|
||||
- `docs/migration/portal-no-iframe/legacy_rewrite_smoke_checklists.md`
|
||||
|
||||
## Shared Guardrails
|
||||
|
||||
- Do not change backend API signatures in rewrite phase.
|
||||
- Keep direct-link behavior and query semantics stable.
|
||||
- If parity fails, rollback to previous stable page artifact before continuing.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Legacy Rewrite Priority Matrix
|
||||
|
||||
## Scoring model
|
||||
|
||||
`priority_score = usage(0-5)*0.3 + complexity(0-5)*0.4 + risk(0-5)*0.3`
|
||||
|
||||
- Usage: current observed operational usage + business criticality.
|
||||
- Complexity: route/API count, frontend LOC, workflow branches.
|
||||
- Risk: data mutation/export/upload sensitivity and regression blast radius.
|
||||
|
||||
## Measured technical baseline
|
||||
|
||||
| Page | Backend route LOC | Template LOC | Frontend LOC | API surface |
|
||||
| --- | ---: | ---: | ---: | --- |
|
||||
| `query-tool` | 509 | 1267 | 3139 | resolve/history/adjacent/associations/equipment/export |
|
||||
| `excel-query` | 355 | 1181 | 624 | upload/schema/query/export |
|
||||
| `job-query` | 195 | 995 | 520 | resources/jobs/txn/export |
|
||||
| `tmtt-defect` | 82 | 271 | 363 | analysis/query + CSV export |
|
||||
|
||||
## Priority scoring
|
||||
|
||||
| Page | Usage | Complexity | Risk | Score |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| `tmtt-defect` | 2 | 1 | 2 | 1.6 |
|
||||
| `job-query` | 3 | 2 | 3 | 2.6 |
|
||||
| `excel-query` | 3 | 4 | 4 | 3.7 |
|
||||
| `query-tool` | 4 | 5 | 5 | 4.7 |
|
||||
|
||||
## Rewrite order decision
|
||||
|
||||
1. `tmtt-defect` (canonical exemplar)
|
||||
2. `job-query`
|
||||
3. `excel-query`
|
||||
4. `query-tool`
|
||||
|
||||
## Rationale
|
||||
|
||||
- Start with lowest-complexity page to establish shared migration playbook.
|
||||
- Keep high-complexity/high-risk `query-tool` last to maximize reuse from prior rewrites.
|
||||
- Defer upload-heavy `excel-query` until shared error/retry/upload patterns are stabilized.
|
||||
@@ -0,0 +1,77 @@
|
||||
# Legacy Rewrite Smoke Checklists (Per-Page)
|
||||
|
||||
本文件是 `7.2 ~ 7.4` 的執行前置與驗收基準。
|
||||
每一頁在「重寫前(wrapper baseline)」與「重寫後(rewrite candidate)」都必須執行同一組 smoke。
|
||||
|
||||
## 0. 執行規則
|
||||
|
||||
- 必須記錄:執行日期、分支/commit、執行人、環境(DEV/UAT)。
|
||||
- 每頁 smoke 通過率要求:`100%`。
|
||||
- 任何 P0 smoke 失敗即視為 `No-Go`,不得進入 wrapper 移除。
|
||||
- `excel-query`、`query-tool` 為 admin/dev 可見頁,需使用 admin 身份執行。
|
||||
|
||||
## 1. `tmtt-defect`(Rewrite Exemplar)
|
||||
|
||||
### 前置條件
|
||||
- 可取得有效 `start_date/end_date` 測試區間。
|
||||
- `/api/tmtt-defect/analysis` 與 `/api/tmtt-defect/export` 可連線。
|
||||
|
||||
### Smoke Cases
|
||||
- [ ] `TMTT-SMOKE-01` Route reachable: `/tmtt-defect` 可直接開啟,無白屏/JS error。
|
||||
- [ ] `TMTT-SMOKE-02` Required params guard: 缺少日期時,顯示明確錯誤且不崩潰。
|
||||
- [ ] `TMTT-SMOKE-03` Query success: 送出合法日期後,KPI/Charts/Detail 皆成功渲染。
|
||||
- [ ] `TMTT-SMOKE-04` Drill-down: 點擊 Pareto 圖欄位可套用/清除篩選,明細同步。
|
||||
- [ ] `TMTT-SMOKE-05` Table behavior: 明細表格可排序,排序方向切換正確。
|
||||
- [ ] `TMTT-SMOKE-06` Export CSV: 匯出成功,response 為 CSV 且檔名包含日期區間。
|
||||
|
||||
## 2. `job-query`
|
||||
|
||||
### 前置條件
|
||||
- `resource` 清單可取得。
|
||||
- 至少有一組可查詢日期區間。
|
||||
|
||||
### Smoke Cases
|
||||
- [ ] `JOB-SMOKE-01` Route reachable: `/job-query` 可直接開啟。
|
||||
- [ ] `JOB-SMOKE-02` Resource loading: `/api/job-query/resources` 回傳清單,UI 可選取。
|
||||
- [ ] `JOB-SMOKE-03` Query jobs: 選設備+日期後可查詢成功並顯示結果。
|
||||
- [ ] `JOB-SMOKE-04` Txn detail: 由查詢結果可開啟某筆 job 的 txn history。
|
||||
- [ ] `JOB-SMOKE-05` Export CSV: 匯出成功且檔案可下載。
|
||||
- [ ] `JOB-SMOKE-06` Validation: 缺日期/無設備/超過上限時回傳明確錯誤訊息。
|
||||
|
||||
## 3. `excel-query`(Admin)
|
||||
|
||||
### 前置條件
|
||||
- 準備一份有效 `.xlsx` 測試檔。
|
||||
- Admin session 已登入。
|
||||
|
||||
### Smoke Cases
|
||||
- [ ] `EXCEL-SMOKE-01` Route/auth: `/excel-query` admin 可進入,非 admin 受保護。
|
||||
- [ ] `EXCEL-SMOKE-02` Upload: 上傳有效 Excel 後可解析欄位與預覽。
|
||||
- [ ] `EXCEL-SMOKE-03` Column detect: 欄位唯一值與型別偵測可正常運作。
|
||||
- [ ] `EXCEL-SMOKE-04` Execute query: 標準查詢與進階查詢都可回傳資料。
|
||||
- [ ] `EXCEL-SMOKE-05` Export CSV: 查詢結果可匯出 CSV。
|
||||
- [ ] `EXCEL-SMOKE-06` Invalid file guard: 非 `.xls/.xlsx` 檔案被拒絕且回傳可讀錯誤。
|
||||
|
||||
## 4. `query-tool`(Admin)
|
||||
|
||||
### 前置條件
|
||||
- Admin session 已登入。
|
||||
- 可用測試 lot/equipment/date range。
|
||||
|
||||
### Smoke Cases
|
||||
- [ ] `QTOOL-SMOKE-01` Route reachable: `/query-tool` 可開啟。
|
||||
- [ ] `QTOOL-SMOKE-02` Resolve flow: lot_id/serial/work_order 至少一種解析成功。
|
||||
- [ ] `QTOOL-SMOKE-03` History flow: lot history 可查詢並顯示。
|
||||
- [ ] `QTOOL-SMOKE-04` Adjacent flow: adjacent lots 查詢可回傳。
|
||||
- [ ] `QTOOL-SMOKE-05` Associations: materials/rejects/holds/splits/jobs 查詢可用。
|
||||
- [ ] `QTOOL-SMOKE-06` Equipment period: status_hours/lots/materials/rejects/jobs 至少各成功一次。
|
||||
- [ ] `QTOOL-SMOKE-07` Export CSV: 匯出可下載且欄位合理。
|
||||
- [ ] `QTOOL-SMOKE-08` Validation: 缺參數、非法日期範圍會回傳可讀錯誤。
|
||||
|
||||
## 5. Exit Rule(與 7.4 連動)
|
||||
|
||||
只有在下列條件全成立,才可移除 wrapper:
|
||||
|
||||
- [ ] 四頁 rewrite smoke 全部通過。
|
||||
- [ ] 與 `legacy_wrapper_telemetry_contract.md` 對照,error 率在門檻內。
|
||||
- [ ] 與 `parity_checklist.md` 的 Route/Workflow/API contract 檢查一致。
|
||||
@@ -0,0 +1,21 @@
|
||||
# Legacy Wrapper Exit Criteria (Rewrite-ready)
|
||||
|
||||
A wrapped page is rewrite-ready only when all criteria are met.
|
||||
|
||||
## Functional readiness
|
||||
|
||||
1. Core workflows are documented with at least one deterministic smoke script per workflow.
|
||||
2. Route/query contract is frozen and covered by contract tests.
|
||||
3. Export/upload side effects (if any) are reproducible in test or staging.
|
||||
|
||||
## Technical readiness
|
||||
|
||||
1. Shared UI and composables can cover at least 70% of page scaffolding (filters, cards, table shell, pagination).
|
||||
2. Required API payload key/type contract is stable for two consecutive releases.
|
||||
3. Wrapper telemetry shows no unresolved high-severity navigation failures in the last release cycle.
|
||||
|
||||
## Operational readiness
|
||||
|
||||
1. Rollback path for rewritten page is documented and rehearsed.
|
||||
2. Error budget and success threshold for canary are defined before rewrite starts.
|
||||
3. Product owner confirms parity acceptance checklist for the target page.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Legacy Wrapper Telemetry Contract
|
||||
|
||||
## Status
|
||||
|
||||
- Retired after wrapper decommission.
|
||||
- `POST /api/portal/wrapper-telemetry` has been removed.
|
||||
- Reference only for historical migration traceability.
|
||||
|
||||
## Wrapper scope
|
||||
|
||||
- `/job-query`
|
||||
- `/excel-query`
|
||||
- `/query-tool`
|
||||
- `/tmtt-defect`
|
||||
|
||||
## Client events
|
||||
|
||||
- `wrapper_loaded`: wrapper route rendered in shell.
|
||||
- `launch`: user clicked "進入既有頁面" and navigation handoff started.
|
||||
|
||||
## API endpoint
|
||||
|
||||
- `POST /api/portal/wrapper-telemetry`
|
||||
- Payload:
|
||||
- `event_type: string`
|
||||
- `route: string` (must be one of wrapper scope routes)
|
||||
- `page_name?: string`
|
||||
- `drawer_name?: string`
|
||||
- `duration_ms?: number`
|
||||
- `ts?: string`
|
||||
|
||||
## Validation
|
||||
|
||||
- Reject unknown routes with `400`.
|
||||
- Reject missing `event_type` with `400`.
|
||||
|
||||
## Fallback behavior
|
||||
|
||||
- Wrapper UI always provides direct anchor navigation to the legacy route.
|
||||
- Telemetry failure must not block navigation.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Motion Baseline Guidelines (Vue Transition First)
|
||||
|
||||
## Baseline principles
|
||||
|
||||
1. Motion clarifies state change, not decoration.
|
||||
2. Default to short transitions (180ms - 240ms) with easing.
|
||||
3. Keep animation on container level (route/panel/filter-chip), avoid animating large table row sets.
|
||||
|
||||
## Standard transitions
|
||||
|
||||
- Route change: `route-fade` (`opacity + translateY`) in portal shell.
|
||||
- Drawer navigation: hover/active transition on sidebar links.
|
||||
- Filter apply/remove: `TransitionGroup` chip enter/leave motion.
|
||||
- Data refresh pulse: panel-level pulse when chart/table refresh is running.
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Respect `prefers-reduced-motion: reduce`.
|
||||
- All key transitions must have non-animated fallback styles.
|
||||
- Motion must not block interaction or delay data rendering.
|
||||
@@ -0,0 +1,19 @@
|
||||
# GSAP Escalation Rule
|
||||
|
||||
## Default
|
||||
|
||||
Use Vue native transitions and CSS transitions for portal migration work.
|
||||
|
||||
## GSAP allowed only when all conditions are true
|
||||
|
||||
1. Interaction cannot be expressed with native Vue/CSS transitions without major maintainability cost.
|
||||
2. Animation is business-critical (e.g., complex timeline playback or synchronized multi-chart storytelling).
|
||||
3. Reduced-motion fallback is explicitly implemented.
|
||||
4. Performance impact is measured on target hardware and meets baseline thresholds.
|
||||
5. A rollback switch exists to disable advanced animation without breaking functionality.
|
||||
|
||||
## Approval checklist
|
||||
|
||||
- Document the exact scenario and why Vue/CSS is insufficient.
|
||||
- Add test coverage for degraded/non-animated path.
|
||||
- Confirm bundle-size impact is acceptable for the target route.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Shared Pagination Migration Batch 1
|
||||
|
||||
## Scope
|
||||
|
||||
Migrated pages/components:
|
||||
|
||||
- `wip-detail/components/LotTable.vue`
|
||||
- `hold-detail/components/LotTable.vue`
|
||||
- `hold-overview/components/LotTable.vue`
|
||||
- `hold-history/components/DetailTable.vue`
|
||||
- `mid-section-defect/components/DetailTable.vue`
|
||||
|
||||
## Change
|
||||
|
||||
- Replaced direct/inline pagination rendering with shared `PaginationControl`.
|
||||
- Preserved existing page event contracts (`prev-page`, `next-page`).
|
||||
|
||||
## Visual parity checks
|
||||
|
||||
- Pagination visibility still depends on `totalPages > 1`.
|
||||
- Prev/Next button enablement remains bounded by page range.
|
||||
- Page info text format remains unchanged on migrated views.
|
||||
|
||||
## Removed duplicated artifacts
|
||||
|
||||
- Removed local Prev/Next markup and boundary logic from `hold-history/components/DetailTable.vue`.
|
||||
- Consolidated pagination behavior into shared wrapper for this batch.
|
||||
46
docs/migration/portal-no-iframe/parity_checklist.md
Normal file
46
docs/migration/portal-no-iframe/parity_checklist.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Portal No-Iframe Migration Parity Checklist
|
||||
|
||||
This checklist is the execution companion for `portal-no-iframe-navigation` migration.
|
||||
|
||||
## A. Drawer Visibility Parity
|
||||
|
||||
- [ ] Non-admin visible drawers/routes match `baseline_drawer_visibility.json` exactly.
|
||||
- [ ] Admin visible drawers/routes match `baseline_drawer_visibility.json` exactly.
|
||||
- [ ] Empty drawers remain hidden.
|
||||
- [ ] `admin_only` drawer behavior remains unchanged.
|
||||
|
||||
## B. Route and Query Contract Parity
|
||||
|
||||
- [ ] `/wip-overview` preserves `workorder|lotid|package|type|status` URL semantics.
|
||||
- [ ] `/wip-detail` preserves `workcenter|workorder|lotid|package|type|status` URL semantics.
|
||||
- [ ] `/hold-detail` preserves required `reason` semantics and fallback behavior.
|
||||
- [ ] `/resource-history` preserves date/granularity/group/family/resource/flag query semantics.
|
||||
|
||||
## C. Core Workflow Smoke Paths
|
||||
|
||||
- [ ] Legacy rewrite per-page smoke checklist passes (`legacy_rewrite_smoke_checklists.md`).
|
||||
- [ ] `/` open portal and switch via drawer navigation.
|
||||
- [ ] `/wip-overview` apply filters and drill down to `/wip-detail`.
|
||||
- [ ] `/wip-overview` reason drill-down to `/hold-detail`.
|
||||
- [ ] `/resource-history` execute query and export path.
|
||||
- [ ] Legacy rewrite pages (`/job-query`, `/excel-query`, `/query-tool`, `/tmtt-defect`) remain reachable and usable.
|
||||
|
||||
## D. API Payload Contract Parity
|
||||
|
||||
- [ ] `/api/wip/overview/summary` required keys present.
|
||||
- [ ] `/api/wip/overview/matrix` required keys present.
|
||||
- [ ] `/api/wip/hold-detail/summary` required keys present.
|
||||
- [ ] `/api/resource/history/summary` required keys present.
|
||||
- [ ] `/api/resource/history/detail` required keys present.
|
||||
|
||||
## E. Stability and Performance
|
||||
|
||||
- [ ] No unhandled JS runtime errors on critical E2E paths.
|
||||
- [ ] Route switch latency remains within agreed threshold.
|
||||
- [ ] Memory footprint does not regress beyond agreed threshold.
|
||||
|
||||
## F. Cutover Decision
|
||||
|
||||
- [ ] All G1~G7 gates are green.
|
||||
- [ ] Rollback rehearsal result is recent and valid.
|
||||
- [ ] Cutover owner and rollback owner are explicitly assigned.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Performance Baseline Comparison
|
||||
|
||||
Measured via Flask test client (route latency in ms).
|
||||
|
||||
## Key Entry Routes
|
||||
|
||||
| Surface | Avg (ms) | P95 (ms) |
|
||||
| --- | ---: | ---: |
|
||||
| Legacy portal `/` | 1.557 | 0.891 |
|
||||
| SPA shell `/portal-shell` | 0.239 | 0.263 |
|
||||
|
||||
## Shared API Route
|
||||
|
||||
| Route | Legacy Avg (ms) | SPA Avg (ms) | Delta (ms) |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| `/api/portal/navigation` | 0.341 | 0.313 | -0.028 |
|
||||
|
||||
## Notes
|
||||
|
||||
- This baseline is synthetic (test client), used for migration regression gate trend tracking.
|
||||
- Production browser/network RUM should be captured separately during canary rollout.
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"portal_spa_enabled": false,
|
||||
"samples_per_route": 15,
|
||||
"metrics": [
|
||||
{
|
||||
"route": "/",
|
||||
"samples": 15,
|
||||
"avg_ms": 1.557,
|
||||
"p95_ms": 0.891,
|
||||
"min_ms": 0.536,
|
||||
"max_ms": 14.997,
|
||||
"status_codes": [
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "/api/portal/navigation",
|
||||
"samples": 15,
|
||||
"avg_ms": 0.341,
|
||||
"p95_ms": 0.396,
|
||||
"min_ms": 0.284,
|
||||
"max_ms": 0.404,
|
||||
"status_codes": [
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "/wip-overview",
|
||||
"samples": 15,
|
||||
"avg_ms": 0.683,
|
||||
"p95_ms": 0.925,
|
||||
"min_ms": 0.422,
|
||||
"max_ms": 2.633,
|
||||
"status_codes": [
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "/resource",
|
||||
"samples": 15,
|
||||
"avg_ms": 0.413,
|
||||
"p95_ms": 0.506,
|
||||
"min_ms": 0.337,
|
||||
"max_ms": 0.699,
|
||||
"status_codes": [
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "/qc-gate",
|
||||
"samples": 15,
|
||||
"avg_ms": 0.422,
|
||||
"p95_ms": 0.453,
|
||||
"min_ms": 0.371,
|
||||
"max_ms": 0.615,
|
||||
"status_codes": [
|
||||
200
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"portal_spa_enabled": true,
|
||||
"samples_per_route": 15,
|
||||
"metrics": [
|
||||
{
|
||||
"route": "/portal-shell",
|
||||
"samples": 15,
|
||||
"avg_ms": 0.239,
|
||||
"p95_ms": 0.263,
|
||||
"min_ms": 0.169,
|
||||
"max_ms": 0.708,
|
||||
"status_codes": [
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "/api/portal/navigation",
|
||||
"samples": 15,
|
||||
"avg_ms": 0.313,
|
||||
"p95_ms": 0.412,
|
||||
"min_ms": 0.257,
|
||||
"max_ms": 0.437,
|
||||
"status_codes": [
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "/job-query",
|
||||
"samples": 15,
|
||||
"avg_ms": 0.904,
|
||||
"p95_ms": 0.786,
|
||||
"min_ms": 0.33,
|
||||
"max_ms": 7.345,
|
||||
"status_codes": [
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "/excel-query",
|
||||
"samples": 15,
|
||||
"avg_ms": 0.47,
|
||||
"p95_ms": 0.448,
|
||||
"min_ms": 0.324,
|
||||
"max_ms": 1.951,
|
||||
"status_codes": [
|
||||
403
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "/query-tool",
|
||||
"samples": 15,
|
||||
"avg_ms": 0.448,
|
||||
"p95_ms": 0.802,
|
||||
"min_ms": 0.32,
|
||||
"max_ms": 0.849,
|
||||
"status_codes": [
|
||||
403
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "/tmtt-defect",
|
||||
"samples": 15,
|
||||
"avg_ms": 0.583,
|
||||
"p95_ms": 0.585,
|
||||
"min_ms": 0.323,
|
||||
"max_ms": 3.455,
|
||||
"status_codes": [
|
||||
200
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
# Portal No-Iframe Migration Rollback Rehearsal Runbook
|
||||
|
||||
## Objective
|
||||
|
||||
Validate that navigation can be restored to pre-cutover stable behavior within target SLO.
|
||||
|
||||
- Target recovery SLO: <= 15 minutes
|
||||
|
||||
## Trigger Conditions
|
||||
|
||||
Execute rollback when any of the following occur after cutover:
|
||||
|
||||
- P0 route unavailable or broken workflow.
|
||||
- Drawer visibility parity mismatch.
|
||||
- Critical API payload contract mismatch causing page failure.
|
||||
- Severe runtime JS errors on critical user paths.
|
||||
|
||||
## Preconditions
|
||||
|
||||
- Feature-flag/env toggle path for shell cutover is in place.
|
||||
- Latest baseline snapshots are available under `docs/migration/portal-no-iframe/`.
|
||||
- On-call owner and rollback owner are assigned.
|
||||
|
||||
## Rehearsal Steps
|
||||
|
||||
1. Enable new navigation mode in staging/canary.
|
||||
2. Execute parity checklist (`parity_checklist.md`) on critical routes.
|
||||
3. Force simulated rollback trigger (toggle off new mode).
|
||||
4. Re-run critical smoke checks:
|
||||
- portal load
|
||||
- drawer visibility
|
||||
- wip overview/detail flow
|
||||
- resource history query path
|
||||
5. Record elapsed recovery time and failures.
|
||||
|
||||
## Verification Criteria
|
||||
|
||||
- Toggle change takes effect without manual code rollback.
|
||||
- Critical routes recover to expected behavior.
|
||||
- Recovery time is within SLO.
|
||||
- No residual hard-failure state remains.
|
||||
|
||||
## Post-Rehearsal Record
|
||||
|
||||
- Date:
|
||||
- Environment:
|
||||
- Operator:
|
||||
- Trigger reason:
|
||||
- Recovery duration:
|
||||
- Issues found:
|
||||
- Follow-up actions:
|
||||
@@ -0,0 +1,28 @@
|
||||
# Rollback Strategy (Shell / Router / Wrapper)
|
||||
|
||||
## Scope
|
||||
|
||||
- Shell entry failures (`/portal-shell`, route guards, navigation API)
|
||||
- Legacy route integration failures (`job-query`, `excel-query`, `query-tool`, `tmtt-defect`)
|
||||
|
||||
## Immediate actions
|
||||
|
||||
1. Flip `PORTAL_SPA_ENABLED=false`.
|
||||
2. Confirm `/` portal route responds and sidebar route links render.
|
||||
3. Verify core routes (`/wip-overview`, `/resource`, `/qc-gate`) return 2xx.
|
||||
4. Verify legacy routes (`/job-query`, `/excel-query`, `/query-tool`, `/tmtt-defect`) return 2xx.
|
||||
|
||||
## Validation checkpoints (<=15 minutes)
|
||||
|
||||
- `GET /api/portal/navigation` returns deterministic drawer/page list.
|
||||
- No spike in 5xx for portal and legacy routes.
|
||||
- Smoke flows for one P0 page and one legacy page pass.
|
||||
|
||||
## Legacy route fallback
|
||||
|
||||
- If a legacy route fails hard, temporarily hide it from drawer config (`page_status.json`) and announce maintenance route.
|
||||
|
||||
## Post-rollback follow-up
|
||||
|
||||
- Capture failed gate(s), timestamp, and impacted routes.
|
||||
- Generate incident summary with fix candidate and rehearsal re-entry criteria.
|
||||
27
docs/migration/portal-no-iframe/rollout_canary_plan.md
Normal file
27
docs/migration/portal-no-iframe/rollout_canary_plan.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Phased Rollout Plan (Canary)
|
||||
|
||||
## Feature switch
|
||||
|
||||
- Primary switch: `PORTAL_SPA_ENABLED`
|
||||
- Canary enabled users/groups are routed to `/portal-shell`; others remain on `/` route-based portal.
|
||||
|
||||
## Phases
|
||||
|
||||
1. Phase A (Internal): dev/admin users only, 1 day.
|
||||
2. Phase B (Canary): 10-20% target users, 2-3 days.
|
||||
3. Phase C (Broad): 50% users if gates are green for 24h.
|
||||
4. Phase D (Full): 100% after cutover gates pass.
|
||||
|
||||
## Success thresholds
|
||||
|
||||
- Route availability (P0): >= 99.9% 2xx/3xx.
|
||||
- Client runtime error rate on critical paths: 0 unhandled exceptions.
|
||||
- Drawer parity drift: 0 mismatches (admin/non-admin route sets).
|
||||
- Wrapper launch success (`launch` telemetry): >= 99%.
|
||||
|
||||
## Error thresholds (rollback trigger)
|
||||
|
||||
- P0 route availability < 99.5% in any 30-minute window.
|
||||
- Any critical workflow smoke failure.
|
||||
- Drawer parity mismatch count > 0 after deployment.
|
||||
- Wrapper telemetry error rate >= 2% sustained 30 minutes.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Shared UI Component Contracts
|
||||
|
||||
## `PaginationControl`
|
||||
|
||||
File: `frontend/src/shared-ui/components/PaginationControl.vue`
|
||||
|
||||
- Props:
|
||||
- `page?: number` (legacy compatibility)
|
||||
- `modelValue?: number`
|
||||
- `totalPages: number`
|
||||
- `infoText?: string`
|
||||
- `visible?: boolean`
|
||||
- Emits:
|
||||
- `update:modelValue(number)`
|
||||
- `change(number)`
|
||||
- `prev(number)`
|
||||
- `next(number)`
|
||||
- Compatibility:
|
||||
- Supports legacy usage (`:page`, `@prev`, `@next`) for migration-safe replacement.
|
||||
|
||||
## `SectionCard`
|
||||
|
||||
File: `frontend/src/shared-ui/components/SectionCard.vue`
|
||||
|
||||
- Slots:
|
||||
- `header`
|
||||
- default body
|
||||
- `footer`
|
||||
- Purpose:
|
||||
- Normalize page section container structure and spacing.
|
||||
|
||||
## `FilterToolbar`
|
||||
|
||||
File: `frontend/src/shared-ui/components/FilterToolbar.vue`
|
||||
|
||||
- Slots:
|
||||
- default filter controls
|
||||
- `actions`
|
||||
- Purpose:
|
||||
- Shared filter layout shell with consistent spacing and action alignment.
|
||||
|
||||
## `StatusBadge`
|
||||
|
||||
File: `frontend/src/shared-ui/components/StatusBadge.vue`
|
||||
|
||||
- Props:
|
||||
- `tone: neutral | success | warning | danger`
|
||||
- `text: string`
|
||||
- Purpose:
|
||||
- Replace repeated local badge/status color snippets.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Shared Composables Contracts
|
||||
|
||||
## `useAutoRefresh`
|
||||
|
||||
File: `frontend/src/shared-composables/useAutoRefresh.js`
|
||||
|
||||
- Current behavior wraps existing `wip-shared` implementation.
|
||||
- Purpose: single import path for all page modules before deeper implementation merge.
|
||||
|
||||
## `useAutocomplete`
|
||||
|
||||
File: `frontend/src/shared-composables/useAutocomplete.js`
|
||||
|
||||
- Current behavior wraps existing `wip-shared` implementation.
|
||||
- Purpose: single import path to normalize field/autocomplete interactions.
|
||||
|
||||
## `usePaginationState`
|
||||
|
||||
File: `frontend/src/shared-composables/usePaginationState.js`
|
||||
|
||||
- State: `page`, `perPage`, `total`, `totalPages`
|
||||
- Derived: `hasPrev`, `hasNext`
|
||||
- Methods: `setFromPayload`, `reset`
|
||||
|
||||
## `useQueryState`
|
||||
|
||||
File: `frontend/src/shared-composables/useQueryState.js`
|
||||
|
||||
- `readQueryState(keys)`
|
||||
- `writeQueryState(nextState)`
|
||||
- Purpose: unify URL query read/write semantics across pages.
|
||||
40
docs/migration/portal-no-iframe/tailwind_design_tokens.md
Normal file
40
docs/migration/portal-no-iframe/tailwind_design_tokens.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Tailwind Design Tokens Mapping
|
||||
|
||||
## Goal
|
||||
|
||||
Map existing portal visual language into a stable token set for phased migration.
|
||||
|
||||
## Color tokens
|
||||
|
||||
- `brand.500` / `brand.600` / `brand.700`: primary brand actions and active navigation states.
|
||||
- `accent.500`: gradient accent endpoint for shell headers.
|
||||
- `surface.app` / `surface.card` / `surface.muted`: app background, card surfaces, muted blocks.
|
||||
- `stroke.soft` / `stroke.panel`: border hierarchy.
|
||||
- `state.success` / `state.warning` / `state.danger` / `state.neutral`: status dots and health states.
|
||||
|
||||
## Typography tokens
|
||||
|
||||
- `fontFamily.sans`: `Noto Sans TC`, `Microsoft JhengHei`, system fallback.
|
||||
|
||||
## Layout tokens
|
||||
|
||||
- `spacing.shell`: outer shell padding.
|
||||
- `spacing.panel`: panel interior spacing.
|
||||
- `spacing.nav`: sidebar item horizontal spacing.
|
||||
- `spacing.block`: vertical rhythm baseline.
|
||||
|
||||
## Radius and elevation tokens
|
||||
|
||||
- `borderRadius.shell`: shell and main card radius.
|
||||
- `borderRadius.card`: smaller control/card radius.
|
||||
- `boxShadow.soft`: light containers (sidebar).
|
||||
- `boxShadow.panel`: content panel container.
|
||||
- `boxShadow.shell`: header gradient card emphasis.
|
||||
|
||||
## Z-index token
|
||||
|
||||
- `zIndex.popup`: status popup / overlay layer.
|
||||
|
||||
## Migration note
|
||||
|
||||
Tokens are intentionally aligned with current portal values to minimize visual drift during iframe decommission.
|
||||
33
docs/migration/portal-no-iframe/tailwind_migration_guide.md
Normal file
33
docs/migration/portal-no-iframe/tailwind_migration_guide.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Tailwind Migration Guide (Portal No-iframe)
|
||||
|
||||
## Purpose
|
||||
|
||||
Move distributed page CSS toward a token-driven Tailwind system without breaking existing portal behavior.
|
||||
|
||||
## Step-by-step
|
||||
|
||||
1. Keep existing route/page behavior unchanged.
|
||||
2. Replace repeated layout wrappers with Tailwind utilities first (`grid`, `flex`, spacing, radius, shadows).
|
||||
3. Replace repeated visual primitives with shared component classes from `@layer components`.
|
||||
4. Move hard-coded colors/spacing to tokens in `tailwind.config.js` and `tailwind.css`.
|
||||
5. Remove obsolete page-local CSS only after visual parity is verified.
|
||||
|
||||
## Recommended migration order
|
||||
|
||||
1. Shell and shared navigation blocks
|
||||
2. Filter bars and KPI card rows
|
||||
3. Shared table containers and pagination controls
|
||||
4. Page-specific edge states and empty/error banners
|
||||
|
||||
## Parity checks per batch
|
||||
|
||||
- Drawer visibility and route links stay unchanged.
|
||||
- Existing URL/query semantics remain compatible.
|
||||
- No new runtime style conflicts in non-admin/admin views.
|
||||
|
||||
## Do / Don’t
|
||||
|
||||
- Do: prefer composable utility classes and shared Vue components.
|
||||
- Do: keep style changes scoped to one route family per batch.
|
||||
- Don’t: introduce new long inline `<style>` blocks in templates.
|
||||
- Don’t: mix unrelated refactors with migration styling tasks.
|
||||
26
docs/migration/portal-no-iframe/tailwind_style_governance.md
Normal file
26
docs/migration/portal-no-iframe/tailwind_style_governance.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Tailwind Style Governance (Migration Phase)
|
||||
|
||||
## Scope
|
||||
|
||||
- Applies to all new frontend work under `frontend/src/**` during iframe removal migration.
|
||||
- Existing page-local CSS can remain temporarily, but new large page-local blocks are disallowed.
|
||||
|
||||
## Rules
|
||||
|
||||
1. New shared UI styles must be authored in Tailwind layers (`base`, `components`, `utilities`) under `frontend/src/styles/tailwind.css`.
|
||||
2. Reusable patterns (cards, filter bars, badge groups, table shells) must use component classes or Vue components, not copy-pasted CSS.
|
||||
3. Page-specific CSS additions over 40 lines require an explicit migration note in the PR and an issue to move them into shared layers.
|
||||
4. Token values must come from `tailwind.config.js` or CSS variables in `tailwind.css`; hard-coded new color scales are disallowed.
|
||||
5. Motion/accessibility styles must support reduced-motion fallback and avoid forced animation on critical data refresh paths.
|
||||
|
||||
## Review Checklist
|
||||
|
||||
- New files import `frontend/src/styles/tailwind.css` through the entry module.
|
||||
- No new iframe-targeting selectors are introduced.
|
||||
- Shared classes/components are reused before adding page-local CSS.
|
||||
- Token naming remains stable (`brand`, `surface`, `stroke`, `state`, spacing/radius/shadow/z-index).
|
||||
|
||||
## Exceptions
|
||||
|
||||
- Bugfix hotfixes may temporarily bypass these rules only if release risk is high.
|
||||
- Every exception must include an expiry task in `openspec/changes/portal-no-iframe-navigation/tasks.md`.
|
||||
44
docs/migration/portal-no-iframe/tmtt_rewrite_exemplar.md
Normal file
44
docs/migration/portal-no-iframe/tmtt_rewrite_exemplar.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# `tmtt-defect` Rewrite Exemplar
|
||||
|
||||
## Scope
|
||||
|
||||
- Route: `/tmtt-defect`
|
||||
- Goal: establish the first canonical legacy rewrite pattern with:
|
||||
- Vue SFC composition
|
||||
- shared UI layer reuse
|
||||
- Tailwind token layer coexistence
|
||||
- no iframe / no wrapper dependency
|
||||
|
||||
## Implemented Structure
|
||||
|
||||
- Entry: `frontend/src/tmtt-defect/main.js`
|
||||
- Page container: `frontend/src/tmtt-defect/App.vue`
|
||||
- Data state/composable: `frontend/src/tmtt-defect/composables/useTmttDefectData.js`
|
||||
- Reusable page components:
|
||||
- `frontend/src/tmtt-defect/components/TmttKpiCards.vue`
|
||||
- `frontend/src/tmtt-defect/components/TmttChartCard.vue`
|
||||
- `frontend/src/tmtt-defect/components/TmttDetailTable.vue`
|
||||
- Shared UI usage:
|
||||
- `frontend/src/shared-ui/components/FilterToolbar.vue`
|
||||
- `frontend/src/shared-ui/components/SectionCard.vue`
|
||||
- `frontend/src/shared-ui/components/StatusBadge.vue`
|
||||
- Backend template mount shell: `src/mes_dashboard/templates/tmtt_defect.html`
|
||||
|
||||
## Behavioral Parity
|
||||
|
||||
The rewrite keeps current route and API contracts:
|
||||
|
||||
- Query API: `GET /api/tmtt-defect/analysis`
|
||||
- Export API: `GET /api/tmtt-defect/export`
|
||||
- Sort/filter/detail behavior preserved on result table
|
||||
|
||||
Smoke coverage references:
|
||||
|
||||
- `TMTT-SMOKE-01` ~ `TMTT-SMOKE-06` in `legacy_rewrite_smoke_checklists.md`
|
||||
|
||||
## Verification Snapshot
|
||||
|
||||
- `npm --prefix frontend run build` passed
|
||||
- `pytest -q tests/test_template_integration.py tests/test_portal_shell_routes.py tests/test_cutover_gates.py tests/test_app_factory.py` passed
|
||||
|
||||
This page is the baseline implementation that remaining legacy rewrites follow.
|
||||
41
docs/migration/portal-no-iframe/ui_pattern_inventory.md
Normal file
41
docs/migration/portal-no-iframe/ui_pattern_inventory.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# UI Pattern Inventory (WIP / Resource / Hold / QC)
|
||||
|
||||
## Duplicated patterns observed
|
||||
|
||||
1. Filter bars:
|
||||
- `hold-overview/components/FilterBar.vue`
|
||||
- `hold-history/components/FilterBar.vue`
|
||||
- `resource-status/components/FilterBar.vue`
|
||||
- `resource-history/components/FilterBar.vue`
|
||||
- `mid-section-defect/components/FilterBar.vue`
|
||||
2. KPI/Summary cards:
|
||||
- `wip-overview/components/SummaryCards.vue`
|
||||
- `wip-detail/components/SummaryCards.vue`
|
||||
- `hold-detail/components/SummaryCards.vue`
|
||||
- `hold-history/components/SummaryCards.vue`
|
||||
- `resource-status/components/SummaryCards.vue`
|
||||
- `resource-history/components/KpiCards.vue`
|
||||
- `mid-section-defect/components/KpiCards.vue`
|
||||
3. Table + pagination shells:
|
||||
- `wip-detail/components/LotTable.vue`
|
||||
- `hold-detail/components/LotTable.vue`
|
||||
- `hold-overview/components/LotTable.vue`
|
||||
- `hold-history/components/DetailTable.vue`
|
||||
- `mid-section-defect/components/DetailTable.vue`
|
||||
- `qc-gate/components/LotTable.vue`
|
||||
4. Multi-select and query controls:
|
||||
- `resource-shared/components/MultiSelect.vue`
|
||||
- `mid-section-defect/components/MultiSelect.vue`
|
||||
5. Repeated status/badge presentation logic:
|
||||
- WIP/Hold status class mapping and local badge styles in multiple tables/cards.
|
||||
|
||||
## Consolidation targets
|
||||
|
||||
- Shared UI layer (`frontend/src/shared-ui/components`)
|
||||
- Shared composables layer (`frontend/src/shared-composables`)
|
||||
- Tailwind tokenized styles (`frontend/src/styles/tailwind.css`)
|
||||
|
||||
## First migration batch completed
|
||||
|
||||
- Unified pagination rendering for WIP/Hold/Mid-section detail tables through `PaginationControl` wrapper.
|
||||
- Auto-refresh and autocomplete imports migrated to `shared-composables` entry points.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Wrapper Decommission Report
|
||||
|
||||
## Decision
|
||||
|
||||
Legacy shell wrapper mode has been decommissioned after rewrite milestone validation.
|
||||
|
||||
## Changes Applied
|
||||
|
||||
- Removed shell wrapper route branch:
|
||||
- `frontend/src/portal-shell/router.js`
|
||||
- `frontend/src/portal-shell/App.vue`
|
||||
- Removed wrapper-specific frontend artifacts:
|
||||
- deleted `frontend/src/portal-shell/constants.js`
|
||||
- deleted `frontend/src/portal-shell/views/LegacyWrapperView.vue`
|
||||
- Removed backend wrapper telemetry endpoint:
|
||||
- deleted `/api/portal/wrapper-telemetry` in `src/mes_dashboard/app.py`
|
||||
|
||||
## Operational Outcome
|
||||
|
||||
- Portal shell navigation now uses direct page-bridge behavior only.
|
||||
- Legacy page access remains available via direct routes.
|
||||
- Wrapper telemetry contract is retired.
|
||||
|
||||
## Validation
|
||||
|
||||
- Route and template integration tests updated and passing.
|
||||
- Cutover gate tests remain green after wrapper removal.
|
||||
Reference in New Issue
Block a user