From 5e5042d717f4f9f9832baa1533bc5bf506eb67be Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Thu, 7 May 2026 18:32:56 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20FE-Admin+Docs:=20PE=20workflow=20N-s?= =?UTF-8?q?tage=20Designer=20+=20UsersPage=20c=E1=BA=A5p=20+=20Docs=20(Chu?= =?UTF-8?q?nk=20F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FE Admin: - types/users.ts: User +positionLevel field + PositionLevel const + PositionLevelLabel/Short maps (NV/PP/TP). - PeWorkflowsPage.tsx Designer extend: InnerStepDto + EditInnerStep types, copyFromDefinition include, departmentsList query, sub-section "Cấp duyệt nhỏ trong phòng" per step card với drag-list { Phòng × Cấp + required } + button "+ Thêm cấp duyệt" emerald + payload include (Order asc). Empty state hint fallback 2-cấp legacy. - UsersPage.tsx: column "Cấp" badge NV/PP/TP emerald (— nếu null) + action button cycle null→1→2→3→null call PATCH /users/{id}/position-level. KHÔNG đụng fe-user — admin-only feature (PeWorkflowsPage + UsersPage ở fe-admin only). Docs: - STATUS.md Last updated + Phase summary count (17→19 mig, 83→89 test, 55→56 bảng) + 1 row Recently Done Session 12 (KEEP narrative cũ). - HANDOFF.md TL;DR Session 12 prepend + 8 cảnh báo Session 13+ + giữ Session phase 2 narrative. - migration-todos.md Phase 9 + Session 12 block 6 chunk + 5 defer task. - session log NEW `2026-05-07-2300-n-stage-workflow.md` đầy đủ rationale + per-chunk + bug log + plan hierarchy. Defer cron audit 2026-06-01: schema-diagram §15 Mig 18 + §16 Mig 19, skill ef-core-migration Mig 18+19 row, skill contract-workflow N-stage cross-ref section. Verify: - npm run build fe-admin pass (✓ built, 0 TS error) - dotnet test 89 pass (no regression) - dotnet build 0 error 🎉 SESSION 12 COMPLETE: N-stage workflow approval Phòng × PositionLevel PE-only. Backward compat 100% với 2-stage Mig 16. 6 commit per-chunk A→F. Total 89 test pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/HANDOFF.md | 56 +++- docs/STATUS.md | 5 +- docs/changelog/migration-todos.md | 20 ++ .../2026-05-07-2300-n-stage-workflow.md | 248 ++++++++++++++++++ fe-admin/src/pages/system/PeWorkflowsPage.tsx | 166 +++++++++++- fe-admin/src/pages/system/UsersPage.tsx | 52 +++- fe-admin/src/types/users.ts | 20 ++ 7 files changed, 558 insertions(+), 9 deletions(-) create mode 100644 docs/changelog/sessions/2026-05-07-2300-n-stage-workflow.md diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index bf2d623..7fb84b9 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -1,6 +1,60 @@ # HANDOFF — Brief 5 phút cho session tiếp theo -**Last updated:** 2026-05-08 00:30 (Session phase 2 wrap-up — **B12-B14 PE detail polish iterate. 3 commit FE-only. 26 commit total session 2026-05-07. 83 test pass. UAT iter mode active.**) +**Last updated:** 2026-05-07 (Session 12 — **N-stage workflow approval Phòng × PositionLevel cấu hình động — Mig 18+19. 6 commit per-chunk: Domain → App CQRS → Service logic → Tests +6 → API → FE Designer + UsersPage. 89 test pass. PE-only first.**) + +## TL;DR Session 12 (07/05 — N-stage workflow approval per phase × dept × cấp) + +User yêu cầu mở rộng từ 2-stage Mig 16 sang N-stage cấu hình động: 1 phase (WorkflowStep cha) có thể cấu hình chuỗi InnerSteps con theo Department × PositionLevel sequential. Mỗi phòng có NV → PP → TP duyệt thứ tự, có thể bypass cùng dept khi role cao + CanBypassReview. + +**6 câu spec defaults chốt:** +- Q1 enum `PositionLevel { NhanVien=1, PhoPhong=2, TruongPhong=3 }` + `User.PositionLevel int?` +- Q2 Sequential pure (Order asc, mỗi inner step = 1 cấp duyệt) +- Q3 TP có CanBypassReview → skip NV+PP cùng dept (audit IsBypassed=true cho cấp dưới) +- Q4 Smart reject về DangSoanThao + RejectedFromPhase + clear N-stage rows tại phase reject +- Q5 PE first (Contract+Budget mirror sau khi PE stable UAT) +- Q6(a) Designer UI 1 sub-section "Cấp duyệt nhỏ trong phòng" drag-list per step + +**6 chunk per-commit:** + +- **Chunk A (`13ab533`)** Domain + Migration 18 — enum PositionLevel + entity InnerStep + ALTER User.PositionLevel + ALTER PEDeptApproval.InnerStepId + EF config Cascade/Restrict + 3-file rule +- **Chunk B (`0e56bd0`)** Application CQRS DTO — extend PeWorkflowStepDto + InnerStepDto + Validator + Handler atomic batch + UserDto +PositionLevel + SetUserPositionLevelCommand (default null backward compat existing PeWorkflowAdminTests) +- **Chunk C (`0c62e24`)** Service N-stage logic + **Migration 19** filtered unique (drop UNIQUE Mig 16 → recreate `WHERE InnerStepId IS NULL` legacy + new `WHERE InnerStepId IS NOT NULL` N-stage). Refactor TransitionAsync: load InnerSteps eager + reject clear rows + hasInnerSteps→N-stage / else→legacy 2-stage. Match firstPending Order asc, exact match upsert 1 row, bypass batch upsert NV+PP+TP audit IsBypassed +- **Chunk D (`3d76c6b`)** 6 test PE N-stage (FirstInner blocks / 3-level sequential pass / TP bypass skips / wrong dept 403 / reject clears rows / legacy fallback no inner). IdentityFixture extend +positionLevel arg. Helper SeedWorkflowDefinitionAsync 2 step adjacent. **83→89 test pass** +- **Chunk E (`83ffabd`)** API `PATCH /users/{id}/position-level` mirror SetBypassReview +- **Chunk F (current)** FE-Admin types/users.ts +positionLevel + PositionLevel const + Label/Short. PeWorkflowsPage Designer + sub-section InnerSteps drag-list { Phòng × Cấp + required } + button "+ Thêm cấp duyệt" emerald + departmentsList query + payload include. UsersPage column "Cấp" badge + cycle button. KHÔNG đụng fe-user (admin-only). Docs/Skill update. + +**Verify:** dotnet build pass + dotnet ef database update Mig 18+19 LocalDB applied + dotnet test 89 pass + npm build fe-admin pass. + +**Cumulative sau Session 12:** + +| | Trước S12 | Sau S12 | +|---|---:|---:| +| BE LOC | ~14850 | ~15300 (+450 — Domain enum + entity + EF config + Service N-stage + App CQRS) | +| Migrations | 17 | **19** (+Mig 18 + Mig 19) | +| DB tables | 55 | **56** (+1 PEWorkflowStepInnerSteps) | +| DB columns mới | — | +2 (User.PositionLevel + PEDeptApproval.InnerStepId) | +| API endpoints | ~133 | **~134** (+1 PATCH /users/{id}/position-level) | +| FE pages | 32 | 32 (no change — extend existing 2 page) | +| Tests | 83 | **89** (+6 N-stage PE) | +| Commits | (after S11+++) | **+6** (A→F per-chunk) | + +## ⚠️ CẢNH BÁO Session 13+ + +1. **N-stage chỉ áp PE first** — Contract + Budget vẫn 2-stage Mig 16 cũ. Mirror sau khi UAT PE 2-3 tuần ổn (giữ pattern, lặp lại Domain entity + Service logic + Tests). + +2. **User.PositionLevel chưa seed cho 30 demo user hiện có** — admin phải set tay qua UsersPage cycle button. Hoặc seed migration sau (mapping Position text → PositionLevel int). + +3. **Designer InnerSteps optional** — workflow definition KHÔNG có InnerSteps fallback logic 2-stage Mig 16 cũ. Backward compat 100%. Khi muốn enable N-stage cho 1 phase: clone version + add inner steps + Save. + +4. **Bypass kế thừa cùng dept only** — TP với CanBypassReview chỉ skip NV+PP CÙNG dept của TP. KHÔNG cross-dept (NV.A bypass không skip NV.B). + +5. **Reset N-stage rows khi reject** — clear chỉ rows tại phase reject. Phase trung gian khác vẫn giữ approvals nếu có. Resume sẽ jump tới RejectedFromPhase + cần re-approve N-stage tại phase đó. + +6. **Wrong-level error message** — đã localize "phòng X cấp Y không khớp". UAT user cần feedback nếu confused. + +7. **schema-diagram §15 Mig 18 + §16 Mig 19** chưa update — defer cho audit cron 2026-06-01 (small drift, không major). + +8. **Skill `ef-core-migration` Mig 18+19 row** chưa add — defer audit 2026-06-01. ## TL;DR Session phase 2 (08/05 — B12-B14 polish iterate sau wrap-up `6e7a6db`) diff --git a/docs/STATUS.md b/docs/STATUS.md index fd86f81..3442a21 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -2,9 +2,9 @@ > **Update rule:** trước khi bắt đầu 1 task → ghi row vào `🔥 In Progress`. Xong → chuyển sang `✅ Recently Done`. -**Last updated:** 2026-05-08 00:30 (Session phase 2 wrap-up — **B12-B14 PE detail polish: Lưu no-close + Xóa phiếu soft-delete + header simplify + NCC col name + winner column highlight + loading overlay/spinner + InfoTab auto re-edit. 3 commit FE-only. 26 commit total session 2026-05-07.**) +**Last updated:** 2026-05-07 (Session 12 — **N-stage workflow approval Phòng × PositionLevel cấu hình động (Mig 18+19): Domain enum + entity + EF + Migration → App CQRS DTO + UpdateUserPositionLevel → Service logic N-stage + legacy 2-stage fallback + smart reject reset → 6 test mới → API endpoint → FE Designer InnerSteps sub-section + UsersPage cột "Cấp". 6 commit per-chunk.**) -## 📍 Phase hiện tại: **Phase 9 active — UAT** — **55 DB tables, 17 migrations, ~133 API endpoints, 32 FE pages. 83 unit test pass** (54 Domain + 29 Infra). 41 gotcha. 30 demo user. 6 skill. **5 PE display status** (Bản nháp / Đã gửi duyệt / Trả lại / Đã duyệt / Từ chối). **9 PE phase enum** (+TraLai = 98). +## 📍 Phase hiện tại: **Phase 9 active — UAT** — **56 DB tables, 19 migrations, ~134 API endpoints, 32 FE pages. 89 unit test pass** (54 Domain + 35 Infra). 41 gotcha. 30 demo user. 6 skill. **5 PE display status** (Bản nháp / Đã gửi duyệt / Trả lại / Đã duyệt / Từ chối). **9 PE phase enum** (+TraLai = 98). **N-stage workflow** Phòng × PositionLevel (NV/PP/TP) cấu hình động per WorkflowStep cha — Mig 18+19. ### 🌐 Production URLs @@ -61,6 +61,7 @@ | Ngày | Ai | Task | Commit | |---|---|---|---| +| 2026-05-07 | Claude | **🎯 SESSION 12 — N-stage workflow approval Phòng × PositionLevel cấu hình động (PE-only first, 6 commit per-chunk + Mig 18+19)** — User yêu cầu mở rộng từ 2-stage Mig 16 (NV.Review/TPB.Confirm) sang N-stage cấu hình động: mỗi WorkflowStep cha (= 1 phase) có thể cấu hình chuỗi InnerSteps con theo Department × PositionLevel với Order sequential. Spec defaults chốt 6 câu (PositionLevel int 1=NV/2=PP/3=TP, sequential pure, bypass cùng dept TP skip NV+PP, smart reject reset N-stage rows về DangSoanThao, PE-only first, designer 1 sub-section InnerSteps). 6 chunk per-commit (build + ef + test pass mỗi chunk per `feedback_per_chunk_commit.md`): **Chunk A (`13ab533`)** Domain enum `PositionLevel` (NV/PP/TP) + entity `PurchaseEvaluationWorkflowStepInnerStep` + ALTER User.PositionLevel int? + ALTER PEDeptApprovals.InnerStepId Guid? + EF config + **Migration 18** `AddPeWorkflowInnerStepsAndPositionLevel` (1 CREATE TABLE + 2 ALTER + 3 index + FK Cascade Step / Restrict Dept/InnerStep). 3-file rule. **Chunk B (`0e56bd0`)** Application CQRS DTO — `PeWorkflowStepInnerStepDto` + extend `PeWorkflowStepDto` + `CreatePeWorkflowStepInnerStepInput` (default null backward compat existing PeWorkflowAdminTests) + Validator child rules + Handler atomic batch insert + UserDto +PositionLevel field + `SetUserPositionLevelCommand` mirror SetBypassReview. **Chunk C (`0c62e24`)** Service N-stage logic — **Migration 19** `AlterPeDeptApprovalsUniqueFilteredForInnerSteps` (filtered unique: legacy `WHERE InnerStepId IS NULL` + N-stage `WHERE InnerStepId IS NOT NULL`) cho phép multi-row cùng dept khác inner step. PurchaseEvaluationWorkflowService refactor: load definition InnerSteps eager + reject branch clear N-stage rows + dept block split hasInnerSteps→N-stage logic / else→legacy 2-stage. N-stage flow: yêu cầu actor có DeptId+PositionLevel, match firstPending (Order asc IsRequired) same dept + (exact level OR canBypass + level≥), exact match upsert 1 row InnerStepId, bypass batch upsert NV+PP+TP cùng dept ≤ actor level (audit IsBypassed cho cấp dưới skip), recheck stillPending → BLOCK + log "duyệt cấp X (còn Y pending)" / all done → fall through phase transition. Backward compat: workflow no InnerSteps fallback legacy + InnerStepId=null filter unique cũ vẫn enforce. **Chunk D (`3d76c6b`)** Tests N-stage 6 test mới (NV first blocks / 3-level sequential pass / TP bypass skips / wrong dept throws 403 / reject clears rows / legacy fallback no inner) + IdentityFixture extend `+positionLevel` arg + helper SeedWorkflowDefinitionAsync 2 step adjacent (ChoPurchasing+ChoCCM) cho FromDefinition build transition policy guard pass. Total 83→**89 test pass**. **Chunk E (`83ffabd`)** API `PATCH /users/{id}/position-level` mirror SetBypassReview pattern + body `{positionLevel:int?}` Authorize Users.Update. **Chunk F (current)** FE-Admin types/users.ts +positionLevel field + PositionLevel const + Label/Short maps. PeWorkflowsPage Designer extend: InnerStepDto type + EditInnerStep type + copyFromDefinition include + departmentsList query + sub-section "Cấp duyệt nhỏ trong phòng" per step card với drag-drop list { Phòng × Cấp + required checkbox } + button "+ Thêm cấp duyệt" (xanh emerald) + payload include innerSteps Order asc. UsersPage column "Cấp" badge NV/PP/TP emerald + action button cycle null→1→2→3→null call positionLevelMut PATCH. KHÔNG đụng fe-user (admin-only feature). Docs/Skill update. PE-only first. Backward compat 100%: workflow no InnerSteps + data legacy 2-stage rows không phá. | `13ab533` (A) · `0e56bd0` (B) · `0c62e24` (C) · `3d76c6b` (D) · `83ffabd` (E) · (current F) | | 2026-05-08 00:30 | Claude | **🎯 SESSION PHASE 2 WRAP-UP — B12-B14 PE detail polish iterate (3 commit FE-only sau wrap-up `6e7a6db`)** — User UAT iteration tiếp, áp rule strict verify khi rename/remove (lesson hotfix CI). 3 batch nhỏ: **B12 (`378c993`)** "Lưu" no-close (chỉ toast + invalidate, KHÔNG đóng workspace) + nút "Xóa phiếu" red bottom CHỈ Bản nháp (soft-delete `IsDeleted=true` qua AuditableEntity, không xóa hoàn toàn DB) + bỏ header bar workspace mode "Sửa header"/"Xóa"/"Đóng" (chuyển hết xuống bottom action bar) + Section 4 column header dùng `s.supplierName` thay `displayName` (NCC master) + Section 3 row chặn xóa NCC khi đã có quotes (`hasQuotes` computed) + tooltip "xóa báo giá trước". **B13 (`e320027`)** InfoTab `useEffect` watch `[autoEdit, canEdit, ev.id, ...]` → re-trigger edit mode khi pencil click phiếu khác (fix useState mount-time only) + sync values từ ev mới (tránh stale state) + Pencil "sáng lên" active state khi `editingRowId === p.id` (bg-brand-100 + text-brand-700 + ring-brand-300 + shadow-sm + tooltip cập nhật) + wire `editingRowId={autoEditHeader ? selectedId : null}` từ Workspace → PeListPanel. **B14 (`d2306b8`)** QuoteDialog bỏ checkbox "Chọn NCC này cho hạng mục" (consolidate winner ở Section 2.a NccSelectorRow, isSelected vẫn gửi BE giữ trạng thái cũ) + winner column Section 4 matrix highlight emerald (header `bg-emerald-50` + `✓ ` prefix + cells `bg-emerald-50 font-semibold` cho ENTIRE column, không chỉ cell có quote) + loading overlay full-screen QuoteDialog (`bg-white/70 backdrop-blur-sm` + spinner ring brand-600 + text "Đang lưu báo giá…"/"Đang xóa…") + NccSelectorRow inline spinner "Đang chọn NCC + sync cột giá Section 4…" + disable Hủy/Xóa/Lưu buttons khi `isSaving`. Verify: `npm run build` × 2 app pass mỗi commit · `dotnet test` 83 pass (KHÔNG regression). | `378c993` (B12) · `e320027` (B13) · `d2306b8` (B14) | | 2026-05-07 | Claude | **🎯 SESSION WRAP-UP S10-11+++++++ — PE Workspace UX overhaul đầy đủ (23 commit / ~3500 LOC FE + Mig 17 BE)** — User UAT live mode iterate liên tục, áp rule `feedback_uat_skip_verify` (skip dotnet test sau mỗi chunk, push ngay). 7 batch chính: **B1 (S10) PE Thao tác 2-panel workspace** — leaf `Pe_*_Create` từ page Create header riêng → workspace 2-panel `[320px_1fr]` mirror HĐ Thầu phụ; PeListPanel pure picker + sticky "+ Thêm mới"; PeDetailTabs `mode='workspace'` ẩn Workflow/Approvals/History + Section 5 disabled "nhập khi duyệt" (4 commit). **B2 (S11) Migration 17** `AddManualBudgetFieldsToPeAndContract` — 4 ALTER (PE + HĐ × `BudgetManualName` nvarchar(200) + `BudgetManualAmount` decimal(18,2)) cho fallback "user nhập tay khi không link Budget entity approved". Domain + EF config + App CQRS Create/Update + DTO + Validator + carry-forward `CreateContractFromEvaluation`. FE toggle "Nhập tay" trong PeHeaderForm + ContractCreatePage NewForm/EditForm × 2 app (5 commit). **B3 (S11+) BudgetFieldRow inline editor** — Section 2 "b. Ngân sách" thay FormRow tĩnh → editable component (toggle + Select OR 2 input + Save dirty + Hủy). canEdit cho cả 3 view (Workspace/Danh sách/Duyệt mode), readOnly chỉ display (3 commit). **B4 (S11++) InfoTab inline edit + PeListPanel pencil hover** — Section 1 "✎ Sửa" button flip display↔inputs (Tên/Địa điểm/Mô tả/Payment editable, Dự án locked). PeListPanel thêm pencil icon group-hover absolute right + URL `?editHeader=1` chain → `autoEditHeader` prop trigger mount-time edit (3 commit). **B5 (S11+++) Workspace "new" sectioned create view** — `PeWorkspaceCreateView.tsx` ~230 LOC layout 5 sections giống PeDetailTabs visual. S1 + S2.b editable, S3-5 LockedHint "Lưu phiếu trước". POST trigger create. Replace `PeHeaderForm` trong workspace mode='new' (1 commit). **B6 (S11++++) Danh sách disable toàn bộ interactions** — PurchaseEvaluationsListPage `readOnly={true}` hardcoded cho PeDetailTabs + `readOnly={!pendingMe}` cho PeWorkflowPanel (List view → ẩn Chuyển tiếp + show hint "Vào menu Duyệt"; Pending vẫn approve được) (2 commit). **B7 (S11+++++) Lock Loại quy trình + payment preset** — workspace `` theo URL `?type=N`. `