wal: flush 20260729T1144
This commit is contained in:
@ -27,7 +27,16 @@
|
||||
|
||||
## Stages
|
||||
|
||||
- [!] **T1 — FE Tổng quan pipeline** (`frontend-designer`, fe-user ONLY) — ĐANG CHẠY background
|
||||
- [x] **T1 — FE Tổng quan pipeline** ✅ — `frontend-designer` land `UserDashboardPage.tsx` **585+/143−** (1 file, 0 dep mới, khai `NO-SCREENSHOT` trung thực); return dính #53 nhưng **file chính + sub-file 15.803B đã trên đĩa TRƯỚC khi chết** — mất 0 B. ⭐ Designer **BÁC 2 dữ kiện lead đưa** (đọc BE thật): `pendingMe` KHÔNG phải query-param (là endpoint `/inbox` mảng phẳng) · `Paged<T>` field là `total` không phải `totalCount`.
|
||||
- [x] **T4a — reviewer diff** ✅ `PASS_WITH_FLAGS — 7 FLAG (0H/2M/5L)` (`sub-reviewer-diff-tongquan.md` 16.761B; §A 12-claim verify độc lập + 7 giả-thuyết-tự-bác; chết #53 ×1 giữa chừng, resume sạch). **Disposition TỪNG DÒNG:**
|
||||
· FLAG-1 [M] aria trùng tên → **FIX**: prop `group` BẮT BUỘC + `aria-label` (TS tự bắt đủ 6 call-site)
|
||||
· FLAG-2 [M] 2 chuỗi Anh → **FIX**: "Toàn trình 4 giai đoạn" · "Tải lên bản hợp đồng đã ký cứng"
|
||||
· FLAG-3 [L] câu GĐ2 nói quá → **FIX**: bỏ mệnh đề "sẵn sàng nối tiếp"
|
||||
· FLAG-5 [L] queryKey đôi → **FIX SÂU HƠN đề nghị**: 🔴 đề nghị gốc (dùng chung key, giữ queryFn trả `.length`) sẽ **VỠ CACHE** InboxPage (cache mảng ⟂ number cùng key) — fix đúng = chung key + chung queryFn (mảng) + `select: d => d.length`; request +2→+1
|
||||
· FLAG-4 [L] đích `/inbox` số lệch nguồn → **PARK** (lệch có sẵn pre-diff, fix cần param/endpoint mới — ngoài scope, ghi nợ)
|
||||
· FLAG-6 [L] 2 nút Thử-lại trùng → **PARK** (reviewer tự đính chính giữa chừng; cosmetic, chấp nhận)
|
||||
· FLAG-7 [L] nhãn GĐ4 → **NO_CHANGE** (đúng spec owner ❷/❹ — GĐ4 cố ý ngoài hệ thống)
|
||||
- [x] **T4b — build độc lập SAU mọi edit** ✅ PASS 454ms (`tsc -b && vite build`) · 6/6 `group` land · diagnostics IDE giữa chừng = snapshot dở (#68), bác bằng compiler thật
|
||||
- [x] **T2 — Prod menu data** ✅ **LIVE + VERIFY TRỌN** (`prod-backup-pre-apply.txt` → apply `hide=3 upd=99 ins=376` → `prod-verify-post-apply.txt`):
|
||||
· Inspect lộ thực-trạng ≠ giả định: Master subtree có matrix 13-row/key (canread 2/13) nhưng **`Ct_*` 28 key = 0 ROW** (chưa từng seed), `Contracts` root 1 row ⇒ INSERT 376 + UPDATE 99, số khớp toán từng đồng (9×11=99 · 28×13+12=376)
|
||||
· Verify: **38/38 key min=max=13 canread** · 3 root `IsVisible=0` · control âm **Pe_* 197/185 NGUYÊN** (❹ "để như cũ") · `Hrm_Config*` canread=7 nguyên (vùng đang-làm loại khỏi grant, đúng ý ❸)
|
||||
|
||||
@ -46,4 +46,78 @@
|
||||
|
||||
## B — FLAG
|
||||
|
||||
<!-- IN PROGRESS — file này hợp lệ giữa chừng; dòng END ở cuối mới là chốt -->
|
||||
### FLAG-1 [M] — a11y AA: hai nút khác đích nhưng TRÙNG tên đọc được
|
||||
|
||||
`fe-user/src/pages/UserDashboardPage.tsx:449` (GĐ1 → `/purchase-evaluations?pendingMe=1`) và `:495` (GĐ3 → `/inbox`) đều là `<button>` có tên khả truy cập **y hệt nhau**: "Chờ tôi duyệt". Thêm nút thứ ba cùng nhãn ở KPI (`:538`). Ngữ cảnh phân biệt (tiêu đề "Duyệt NCC" / "Duyệt Hợp đồng") chỉ tồn tại **bằng thị giác** — `<h3>` của thẻ giai đoạn (`:153`) không được liên kết chương trình với nhóm nút, nên người dùng trình đọc màn hình duyệt theo danh sách nút sẽ thấy 2-3 mục trùng tên dẫn tới 3 nơi khác nhau. Spec yêu cầu a11y AA nên đây là khoản nợ thật, không phải chuyện thẩm mỹ.
|
||||
**Fix đề nghị:** thêm `aria-label` đầy đủ cho `StageStatRow` (ví dụ `aria-label={`${title} — ${label}`}`), hoặc bọc cụm số liệu bằng `role="group"` + `aria-labelledby` trỏ vào `id` của `<h3>` giai đoạn.
|
||||
|
||||
### FLAG-2 [M] — lọt chữ tiếng Anh ra giao diện (ràng buộc "100% tiếng Việt")
|
||||
|
||||
Tôi rút toàn bộ chuỗi hiển thị MỚI từ diff (`git diff | grep '^+' | grep -oE '>...<|label="..."|title="..."|description="..."'`) rồi soi từng chuỗi. Có đúng **2 chuỗi lọt tiếng Anh**:
|
||||
- `:405` — `<h2>Pipeline 4 giai đoạn · 21 bước</h2>`: **"Pipeline"**. Nó lại nằm ngay dưới dòng eyebrow "Quy trình mua sắm → ký kết hợp đồng" (`:402-404`) nên vừa là tiếng Anh vừa lặp ý.
|
||||
- `:520` — `description="Upload bản hợp đồng đã ký cứng."`: **"Upload"**.
|
||||
|
||||
Mọi chuỗi còn lại (14 nhãn/tiêu đề) đều thuần Việt, kể cả "Giai đoạn {n}", "Sắp triển khai", các thông báo lỗi. Lưu ý bối cảnh: "upload các file đã ký cứng" là chữ owner dùng trong lời thoại gốc (`run.md:10`), nên đây là câu hỏi biên tập cho owner chứ không phải implementer làm sai ý; còn "Pipeline" thì owner chỉ nói trong lời trao đổi nội bộ (`run.md:14` — "Pipeline 4 GĐ đầy đủ"), không phải chốt nhãn hiển thị.
|
||||
**Fix đề nghị:** `:405` → "Quy trình 4 giai đoạn · 21 bước" (bỏ luôn trùng ý với eyebrow); `:520` → "Tải lên bản hợp đồng đã ký cứng." — hoặc owner xác nhận giữ nguyên chữ "Upload".
|
||||
|
||||
### FLAG-3 [L] — câu "sẵn sàng nối tiếp" ở GĐ2 nói QUÁ điều dữ liệu chứng minh được
|
||||
|
||||
`:467-471` — dòng gợi ý của GĐ2 in `${peApproved} phiếu Duyệt NCC đã duyệt sẵn sàng nối tiếp`, trong đó `peApproved` là **tổng mọi phiếu `phase=DaDuyet`**. Nhưng phiếu đã duyệt có thể ĐÃ sinh hợp đồng rồi: entity có sẵn cột `PurchaseEvaluation.ContractId` (`src/Backend/SolutionErp.Domain/PurchaseEvaluations/PurchaseEvaluation.cs:31` — *"FK Contracts — set khi user gen HĐ từ phiếu"*), và endpoint `POST /purchase-evaluations/{id}/contracts-from-evaluation` (`PurchaseEvaluationsController.cs:340`) chính là đường sinh ra nó. Vậy con số đang gộp cả phiếu "đã nối tiếp xong" vào nhóm "sẵn sàng nối tiếp" — càng dùng lâu số càng phồng và càng sai nghĩa.
|
||||
Không thể lọc bằng endpoint hiện có (spec cấm thêm endpoint), nên cách rẻ nhất là sửa câu chữ.
|
||||
**Fix đề nghị:** đổi thành "{n} phiếu Duyệt NCC đã duyệt" (bỏ mệnh đề "sẵn sàng nối tiếp"), hoặc chờ GĐ2 có thật rồi thêm tham số lọc `hasContract=false` ở BE.
|
||||
|
||||
### FLAG-4 [L] — bấm số ở GĐ3 sẽ ra màn hình có số KHÁC (lệch có sẵn từ trước, nay bị nhân đôi độ nổi bật)
|
||||
|
||||
Hai ô của GĐ3 lấy số từ `/reports/my-dashboard` nhưng lại dẫn sang `/inbox` — hai nguồn đếm theo hai luật khác nhau:
|
||||
- `:495-501` "Chờ tôi duyệt" = `pendingMyApproval`, handler **loại trừ hợp đồng do chính tôi soạn** (`GetMyDashboardQuery.cs` — `eligiblePhases.Contains(c.Phase) && c.DrafterUserId != userId`). Còn `/contracts/inbox` **không** loại trừ (`ContractFeatures.cs:391-401` chỉ lọc `eligiblePhases.Contains(c.Phase)`), lại còn `.Take(100)`.
|
||||
- `:503-510` "Đã quá hạn" = `overdue` tính trên hợp nhất *hợp đồng tôi soạn* ∪ *hợp đồng chờ tôi duyệt*; phần "tôi soạn" **không bao giờ xuất hiện** trong hộp thư đến.
|
||||
|
||||
Nghĩa là số 3 trên trang chủ có thể mở ra hộp thư 7 dòng, hoặc 0 dòng quá hạn. Đây là lệch **có sẵn trước diff** (thẻ KPI cũ `:542`, `:557` cũng trỏ `/inbox`), nên tôi không tính là hồi quy — nhưng hero vừa nâng nó lên vị trí nổi nhất trang và lặp lại đúng con số đó hai lần trên cùng một màn.
|
||||
**Fix đề nghị:** hoặc đổi đích sang `/my-contracts` cho ô "Đã quá hạn", hoặc để hộp thư nhận tham số lọc (`/inbox?overdue=1`) — nếu chưa làm ngay thì ghi vào backlog, đừng để trôi.
|
||||
|
||||
### FLAG-5 [L] — cùng một request nhưng hai khoá cache, thay vì dùng lại khoá đã có
|
||||
|
||||
`:367-370` tạo `queryKey: ['pipeline-pe-inbox']` cho `GET /purchase-evaluations/inbox`. Nhưng `fe-user/src/pages/InboxPage.tsx:77-78` **đã** có `queryKey: ['pe-inbox']` gọi đúng endpoint đó, đúng bộ tham số đó (không tham số). Hai khoá khác nhau ⇒ hai bản cache độc lập cho cùng một tài nguyên: mỗi lần đi Trang chủ ↔ Hộp thư đều bắn thêm một request, và hai màn có thể hiển thị hai ảnh chụp khác thời điểm.
|
||||
Yêu cầu "queryKey mới không đè cache trang list" vẫn **thoả** (`['pe-list', …]` không bị đụng) — đây là chuyện tiết kiệm và nhất quán, không phải vi phạm spec.
|
||||
**Fix đề nghị:** dùng lại `['pe-inbox']` cho ô "Chờ tôi duyệt" của GĐ1 ⇒ số request thêm rơi từ +2 xuống +1 và hai màn luôn khớp nhau.
|
||||
|
||||
### FLAG-6 [L] — báo lỗi hiện hai lần cho cùng một sự cố
|
||||
|
||||
`:409-420` băng cảnh báo `hasError` gộp cả 5 query, trong đó có `recent` và `recentPe`. Khi đúng một danh sách hỏng, người dùng thấy đồng thời băng vàng ở hero ("Không tải được một vài số liệu") **và** khối lỗi riêng của danh sách đó (`:559-573` cho hợp đồng, phần tương ứng cho phiếu), mỗi chỗ một nút "Thử lại". Không sai chức năng — `retryFailed` chỉ gọi lại đúng query hỏng — nhưng là nhiễu.
|
||||
**Tự đính chính trong lúc viết flag này:** tôi định đề nghị "bỏ `recent`/`recentPe` khỏi `hasError`", nhưng đọc lại thì **sai** — `recent.data.total` nuôi ô "Tổng hợp đồng" (`:488`) và `recentPe.data.total` nuôi ô "Tổng phiếu" (`:434`), tức hai query đó thực sự thuộc hero. Băng cảnh báo phủ chúng là ĐÚNG.
|
||||
**Fix đề nghị (đã sửa theo đúng sự thật):** giữ nguyên `hasError`, chỉ bỏ bớt một trong hai nút "Thử lại" trùng nhau — hoặc chấp nhận trùng vì nó vô hại. Mức L, không chặn commit.
|
||||
|
||||
### FLAG-7 [L] — GĐ4 gắn nhãn "Sắp triển khai" trong khi phần khung của nó đã chạy thật (việc của owner quyết, không phải lỗi người làm)
|
||||
|
||||
`:519-521` đánh GĐ4 "Ký cứng & phát hành (Bước 19 → 21)" là chưa triển khai. Trên thực tế module Hợp đồng đã có sẵn hai phase cuối `DangDongDau(8) → DaPhatHanh(9)` cùng chức năng đính kèm bản scan đã ký (`POST /contracts/{id}/attachments`). Người dùng đang dùng hai phase này mỗi ngày có thể ngơ ngác khi trang chủ bảo phần đó "sắp triển khai".
|
||||
Diff **khớp đúng spec** ở điểm này — owner tự chốt GĐ4 nằm ngoài hệ thống (`run.md:10` và bảng `run.md:26`: *"ngoài hệ thống — upload file đã ký (Contract attachments sẵn nền); thiết kế sau"*) — nên tôi ghi nhận là điểm cần owner biết, **không** tính là implementer làm sai.
|
||||
**Fix đề nghị:** nếu owner muốn tránh hiểu nhầm, đổi mô tả GĐ4 thành "Hiện làm ngoài hệ thống — đính kèm bản đã ký vào hồ sơ hợp đồng" thay vì chỉ badge "Sắp triển khai".
|
||||
|
||||
---
|
||||
|
||||
## C — Quan sát KHÔNG tính FLAG (ghi để khỏi bị đào lại)
|
||||
|
||||
- **Mũi tên nối `text-slate-400` (~2,6:1)** ở `:165-166`: KHÔNG vi phạm AA vì cả hai icon đều `aria-hidden` và thứ tự giai đoạn đã được mã hoá bằng badge chữ "Giai đoạn 1..4"; đây là đồ hoạ trang trí thuần tuý.
|
||||
- **Nợ FD tự khai** (chiều cao dải pipeline ở tablet 768–1023px; số đỏ "quá hạn" nằm trong thẻ teal): tôi không phản bác được bằng cách soi tĩnh — cần mắt người trên prod. Ghi nhận là nợ đã khai minh bạch, không phải giấu.
|
||||
- **Trùng số liệu hero ⟂ KPI**: "Chờ tôi duyệt" và "Đã quá hạn" nay xuất hiện hai lần trên một màn. Owner đã yêu cầu giữ KPI cũ nên đây là hệ quả cố ý, không phải sơ suất.
|
||||
|
||||
---
|
||||
|
||||
## D — Kết luận
|
||||
|
||||
**Verdict: PASS_WITH_FLAGS — 7 FLAG (0 H / 2 M / 5 L). Không có gì chặn commit.**
|
||||
|
||||
Bốn trục soi được lệnh giao, kết quả từng trục:
|
||||
|
||||
| Trục | Kết quả | Căn cứ |
|
||||
|---|---|---|
|
||||
| Đúng-spec (4 nhãn + ranh bước) | **ĐẠT** | GĐ2 đúng nguyên văn "Đề xuất ký kết hợp đồng" (`:464`, khớp `run.md:13`); ranh 1→6 / 7→12 / 13→18 / 19→21 đúng từng chữ; tổng 21 bước cộng lại khớp. GĐ2/GĐ4 dùng badge xám nét đứt + biểu tượng đồng hồ cát, KHÔNG dùng đỏ/vàng ⇒ đọc ra "kế hoạch", không đọc nhầm thành lỗi. |
|
||||
| Data đúng | **ĐẠT** | `phase=7` đúng là Đã-duyệt; `pendingMe` được xử lý đúng bản chất (endpoint riêng trả mảng, không phải query-param); đọc `total` đúng tên field của `Paged<>`; không có phép chia nào nên không sinh NaN; skeleton chỉ nằm trong từng dòng số, khung 4 giai đoạn luôn hiện. |
|
||||
| Không hồi quy KPI cũ | **ĐẠT** | Đủ 5 trường `MyDashboardDto` (`:532`, `:539`, `:547`, `:555`, `:562`) + đủ 2 danh sách gần đây, lại còn thêm nhánh báo lỗi mà bản cũ thiếu. |
|
||||
| Vệ sinh query / TS / React | **ĐẠT** | Khoá cache mới không đụng khoá cũ; +2 request (trần 3); `refetchOnWindowFocus:false` nên không bão request; named export `UserDashboardPage` giữ nguyên; 0 import từ `fe-admin`; 0 import thừa (tôi đối chiếu từng icon với chỗ dùng). |
|
||||
|
||||
**Kiểm chứng độc lập tôi tự chạy (không tin lời khai của FD):** `npx tsc --noEmit -p tsconfig.app.json` trong `fe-user` → **exit 0, output 0 byte**. Đây là bản chạy riêng của tôi, không dùng lại kết quả build 506 ms mà lead đã chạy.
|
||||
|
||||
**Giới hạn phải khai:** không live-verify được (route `/dashboard` đòi đăng nhập, FE chưa deploy). Mọi kết luận về bố cục, tương phản thực tế và hành vi runtime là **suy luận từ mã nguồn**, chưa có một khung hình nào được nhìn bằng mắt.
|
||||
|
||||
<!-- END sub-reviewer-diff-tongquan · TOTAL=7 FLAG -->
|
||||
|
||||
Reference in New Issue
Block a user