Files
solution-erp/.claude/workflows/runs/2026-07-29-S159-tong-quan-pipeline-menu/sub-reviewer-diff-tongquan.md
2026-07-29 11:36:35 +07:00

50 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# sub-reviewer-diff-tongquan — soi đối kháng diff `fe-user/src/pages/UserDashboardPage.tsx` (S159 L8)
> Run: `2026-07-29-S159-tong-quan-pipeline-menu` · vai: reviewer (adversarial, READ-only)
> Vật soi: `git diff -- fe-user/src/pages/UserDashboardPage.tsx` — 728 dòng (585+/143), 1 file.
> 🔴 Ghi đĩa TRONG LÚC LÀM (chống #53). Mỗi FLAG ghi ngay khi tìm ra, không đợi cuối.
> 🔴 Giới hạn đã khai: **KHÔNG live-verify** (route `/dashboard` cần login, FE chưa deploy) ⇒ toàn bộ kết luận là **soi TĨNH** trên diff + đọc file nguồn BE/FE + đọc route/type. Mọi phát biểu về hành vi runtime là **suy luận từ mã**, đã đánh dấu rõ chỗ nào chưa chứng được.
---
## 0 — Xác nhận phạm vi (đã chạm đĩa, không tin lời khai)
- `git diff --stat`**đúng 1 file** `fe-user/src/pages/UserDashboardPage.tsx`, 585+/143. ✅ khớp lệnh.
- `git status --porcelain` → các file M khác (`WAL.md`, `docs/STATUS.md`, `docs/HANDOFF.md`, `scripts/*`, `.claude/commands/*`) **không thuộc vật soi**, đúng như FD khai là của lead. **0 file `fe-admin/**`** bị chạm. ✅ ràng buộc "fe-user ONLY" THOẢ.
- Named export `UserDashboardPage` giữ nguyên (dòng context không đổi trong hunk `@@ -113,20 +351,180 @@` phía trên). ✅
- 0 dependency mới (chỉ thêm icon `lucide-react` đã có sẵn). ✅
---
## A — Những claim của FD tôi đã ĐỘC LẬP kiểm và XÁC NHẬN ĐÚNG (không phải tin lời)
| Claim | Kiểm bằng | Kết quả |
|---|---|---|
| `Paged<T>``total`, **không**`totalCount` | `fe-user/src/types/master.ts:1-9` | ✅ ĐÚNG — lead brief ghi `totalCount`**SAI**, FD đính chính đúng |
| `phase` là query-param BE thật | `PurchaseEvaluationsController.cs:23` `[FromQuery] PurchaseEvaluationPhase? phase` | ✅ |
| `PurchaseEvaluationPhase.DaDuyet === 7` | `fe-user/src/types/purchaseEvaluation.ts:28` | ✅ |
| `pendingMe` KHÔNG phải query-param BE; `/inbox` trả **mảng phẳng** | `PurchaseEvaluationsController.cs:30-35` trả `List<...>` (không `PagedResult`) | ✅ — dùng `.length` là đúng |
| `/reports/my-dashboard` chỉ đếm bảng **Contracts** ⇒ gán GĐ3 đúng nghĩa | `GetMyDashboardQuery.cs` — mọi query đi từ `db.Contracts` | ✅ |
| `.icon-chip` có size nội tại 2.25rem ⇒ bỏ `h-9 w-9` là no-op | `index.css:128-137` | ✅ |
| `h1..h4` + `.stat-value` là rule **unlayered** ⇒ phải dùng `!` để đè màu | `index.css:79-83` + `:140-146` — cả 2 set `color`, **KHÔNG set `font-size`** | ✅ và FD áp `!` ĐÚNG CHỖ: `text-brand-800!` / `text-brand-700!` / `text-accent-600!` / `text-slate-600!`. `text-base` / `text-[15px]` **không** cần `!` vì 2 rule đó không set `font-size` — tôi đã đọc để bác giả thuyết "chữ phình to". |
| Mọi stop màu dùng đều CÓ THẬT trong `@theme` | `index.css:7-51` — brand 50-900 · teal 50/100/500/600/700 · violet · amberx · accent 500/600 | ✅ 0 stop ma (không có `-800` của teal/violet/amberx nào bị dùng) |
| `.label-eyebrow` = brand-600 chỉ 4.42:1 | tự tính lại luminance từ `#1f7dc1` | ✅ 4.41 — số FD khai **đo được và đúng**, né sang `text-brand-700` là hợp lý |
| `slate-500` = 4.76:1 (FD ghi 4.8) | tự tính từ `#64748b` | ✅ |
| `Button``size="sm"` + `gap-1.5` sẵn ⇒ bỏ `mr-1` là đúng | `fe-user/src/components/ui/button.tsx` | ✅ |
| 1 file, 0 file `fe-admin/**`, 0 dep mới, named export giữ | `git diff --stat` + đọc file | ✅ |
**Giả thuyết tôi TỰ ĐẶT RA rồi TỰ BÁC (ghi lại để khỏi ai đào lại):**
- *"Số GĐ1 là toàn hệ thống còn GĐ3 là của riêng tôi ⇒ lệch phạm vi, user đọc sai tỷ lệ rơi rụng"* → **BÁC**: cả 2 endpoint đều IDOR-scoped per-user (`PurchaseEvaluationFeatures.cs:596-614` + `ContractFeatures.cs:297-303`). Cùng phạm vi ⇒ so sánh được.
- *"`Chờ tôi duyệt` GĐ1 = `inbox.length` sẽ LỚN HƠN số ở màn đích vì màn đích lọc thêm client-side `DaGuiDuyet`"* (`PurchaseEvaluationsListPage.tsx:211-212`) → **BÁC**: BE inbox chỉ trả phase ∈ {ChoPurchasing, ChoDuAn, ChoCCM, ChoCEODuyetPA, ChoCEODuyetNCC, ChoDuyet} (`PurchaseEvaluationFeatures.cs:731-742,762`), tất cả đều map về `DaGuiDuyet` (`purchaseEvaluation.ts:105-110`) ⇒ bộ lọc client là no-op, **2 số khớp nhau**.
- *"`?phase=7` / `?pendingMe=1` là URL bịa, màn đích không đọc"* → **BÁC**: `PurchaseEvaluationsListPage.tsx:37,45,95` đọc cả 2. Route `/purchase-evaluations`, `/my-contracts`, `/inbox` đều có thật trong `App.tsx:50,58,59`.
- *"queryKey mới đè cache trang list"* → **BÁC**: trang list dùng `['pe-list', {...}]` / `['pe-detail', id]`; key mới là `['pipeline-pe-approved']` / `['pipeline-pe-inbox']` — không giao.
- *"bắn request bão"* → **BÁC**: `main.tsx:7-14` `refetchOnWindowFocus:false`, `retry:1`; +2 request đúng trần ≤3.
- *"chia 0 / NaN khi rỗng"* → **BÁC**: diff không có phép chia nào; `value ?? 0` chặn undefined.
- *"skeleton che cả pipeline"* → **BÁC**: loading là per-row (`StageStatRow`), khung 4 GĐ luôn render.
---
## B — FLAG
<!-- IN PROGRESS — file này hợp lệ giữa chừng; dòng END ở cuối mới là chốt -->