K2-FE: DONE — build ×2 PASS · SHA-pair 5/5 # sub-implfe-k2 — K2 FRONTEND (S164, wave 2/8, SP-002 phiếu mang hạng-mục + nhóm) > Luật đan-xen (bài K1 + #53): làm mục nào GHI NGAY mục đó. Dòng-1 = verdict, cập nhật CUỐI. > Nguồn: `sub-implbe-k2.md` (BE đã land) + spec-cum1 vá-4 + spec-tổng-quát QĐ-1/OG-1/OG-7 + nền `sub-invest-fable-b2-cum1.md` §K2.f. ## §0 Tiền đề — ĐO TRƯỚC MỌI WRITE (không tin sổ) - `git status --porcelain` @start: `sub-implfe-k2.md` **KHÔNG tồn tại** trong run-folder (có k1/implbe-k2/test/reviewer/cicd) ⇒ Write mới AN TOÀN, không đè lane khác. - **0 file `fe-*/src` nào đang M** trong git status @start ⇒ mọi thay đổi FE dưới đây là của lane này, git-diff sẽ quy trách nhiệm đúng. - BE lane đã land (đọc `sub-implbe-k2.md`): Mig 71 applied 2-DB · rào (PeId,Group) · submit-guard · list `?group=` · Σ-Approved · build 0W/0E · suite 598/0. - 🔴 CẤM theo task: đụng BE/.cs · chạy dotnet (lane test song song) · route/menu mới (K4a) · sửa spec. ### §0.a SHA baseline 5 cặp KHKK FE (TRƯỚC khi sửa) — cả 5 IDENTICAL | File (`fe-*/src/…`) | admin (12 ký tự đầu SHA256) | user | Trạng thái | |---|---|---|---| | `types/khkk.ts` | `8b757f5b1422` | `8b757f5b1422` | IDENTICAL | | `pages/khkk/KhkkCreatePage.tsx` | `ea51e7626eb1` | `ea51e7626eb1` | IDENTICAL | | `pages/khkk/KhkkDetailPage.tsx` | `c8ab2eadd3fd` | `c8ab2eadd3fd` | IDENTICAL | | `pages/khkk/KhkkListPage.tsx` | `40a650bf6964` | `40a650bf6964` | IDENTICAL | | `pages/khkk/KhkkWorkflowPanel.tsx` | `2b64144ef6e8` | `2b64144ef6e8` | IDENTICAL | ⇒ Chiến lược: sửa **fe-admin** → `cp` sang **fe-user** → đo lại SHA (khuôn cookie-cutter, 9× proven). Không có UX riêng 2 app trong 5 file này (đối chứng: baseline identical). ### §0.b Hình dạng DTO BE ĐỌC TỪ MÃ THẬT (không đoán — task ép) - `ContractSigningPlanListItemDto` `ContractSigningPlanFeatures.cs:58-64`: append-CUỐI **`int ApprovalGroup`** + **`decimal TotalApprovedAmount`** (vá-8 — Σ thứ 3). - `ContractSigningPlanLineDto` `:66-79`: append-CUỐI **`Guid? CatalogEntryId`** + **`string? TenHangMuc`** (denorm). - `ContractSigningPlanDetailDto` `:130-166`: append-CUỐI (SAU cả các List) **`int ApprovalGroup`** + **`bool BudgetFrozen`** (display-gate, KHÔNG phải cột DB). - `ApprovedPeAwaitingPlanDto` `:169-191`: append-CUỐI **`List ExistingGroups`** (nhóm của các plan `Phase != TuChoi`; rỗng = PE chưa có phiếu). Picker **KHÔNG còn loại PE-đã-có-phiếu** (`:1291-1295` gỡ vế `!Any(...)`). - `CreateContractSigningPlanCommand` `:333-337`: `PeId, ApprovalWorkflowId, GhiChu?, **int? ApprovalGroup = null**` — **null ⇒ nhóm 1** (`:393`); validator `InclusiveBetween(1,8)` CHỈ khi có giá trị (`:349-353`). - `ListContractSigningPlansQuery` `:700-705` `+int? ApprovalGroup`; controller expose **`?group=`** (BE lane §f). - 409 message create (`:400-402`): *"Phiếu Duyệt NCC này đã có kế hoạch ký kết cho nhóm duyệt N{g}. Chọn nhóm duyệt khác, hoặc xoá/để bị từ chối kế hoạch cũ của nhóm này trước khi lập lại."* > 🔴 **[lead @S165] p1 CHẾT ngay sau §0.b** — 4 dòng markup tool-call rò vào đuôi file (sub-class #53 MỚI: > content-Write lẫn cú pháp invoke), lead đã cắt. Disk-truth p1: fe-admin 4 file M (types +69 · CreatePage +177 > mid-wire [unused: select-nhóm + nút-phụ + badge chưa vào JSX] · Detail +33 · List +1); fe-user CHƯA cp. > p2 tiếp từ đây — append §1 trở xuống. --- # p2 (relaunch @S165) — tiếp từ §0.b ## §1 Wire CreatePage — ĐO ĐĨA TRƯỚC, KHÔNG TIN SỔ 🔴 **Tiền đề của lead SAI MỘT PHẦN (đã đo lại bằng `git diff`, không đọc lại sổ):** lead khai CreatePage "GIỮA CHỪNG WIRE, các khai báo chưa vào JSX". Đo thật trên đĩa (`git diff -- fe-admin/src/pages/khkk/KhkkCreatePage.tsx`, 177 dòng +) thì **8/8 khai báo lead liệt là unused đều ĐÃ có call-site trong JSX**: (số dòng dưới = đo bằng `grep -n` trên file SAU khi p2 xong, không phải trí nhớ) | Khai báo lead nói "chưa vào JSX" | Call-site THẬT trên đĩa (fe-admin) | |---|---| | `KHKK_APPROVAL_GROUPS` | `firstFreeGroup()` `:55` + `.map` dựng 8 `