@ -24,6 +24,7 @@ Giữa **Duyệt NCC** (module PE, kết thúc `PE.DaDuyet`) và **Hợp đồng
### 1.2 Tên + mục đích (owner đặt)
> **anh:** *"Bước này là bước **'đề xuất giá trị ký kết hợp đồng'**"* · sau đó gọi là **"Kế hoạch ký kết Hợp đồng"**.
> 🔄 **OWNER CHỐT NHÃN @S159 (2026-07-29, AskUser ❶):** tên phiếu/menu = **"Đề xuất ký kết hợp đồng"** — mọi label VN trong spec đọc theo tên này (tên bảng/class English `ContractSigningPlan*` GIỮ NGUYÊN). Cùng lượt anh chốt: **ranh GĐ2 = b.7→12** (b.12 chốt con số ∈ GĐ2) · GĐ3 = b.13→18 tận dụng Contract V2 · GĐ4 = b.19→21 + ký cứng **upload-only** · **b.7 email BỎ** khỏi scope, thay bằng tạo-phiếu-từ-PE-`DaDuyet` (= chính thiết kế W2 — **Q8 ĐÓNG: out-of-scope**).
Đây là phát biểu **MỤC ĐÍCH**: đầu ra của khúc này = **MỘT CON SỐ CÓ PHÊ DUYỆT** (giá trị ký kết đề xuất).
b.8-9 (duyệt mẫu / shopdrawing với TVGS) = **CĂN CỨ**; b.10-12 = **chuỗi duyệt chính con số đó**.
# run — S159 arc-2 · Tổng quan pipeline 4 GĐ + điều chỉnh menu eOffice
- **run-id:** `2026-07-29-S159-tong-quan-pipeline-menu` · phiên-LOGIC **L8**, window 1 (arc thứ 2 sau bookend)
- **mode:** RUN-TRACE (≥3 task) · lead = **Fable 5** (anh `/model` — chủ dự án đổi, 0 caveat)
- **Nguồn lệnh:** ảnh chụp eoffice + text anh 2026-07-29 (đánh dấu 3 vùng đỏ) + 4 câu AskUser đã chốt
## 🔴 OWNER CHỐT — NGUYÊN VĂN (2026-07-29, gõ vào sổ ngay để không bốc hơi — bài ring2 M-1 @S158)
**Lệnh khung (text kèm ảnh):**
> *"Hiện điều chỉnh cái giao diện này lại theo thế này. Đổi vai main sang Fable nhé. Hiện là tổng quy trình mình đã đi (…): GĐ1: Duyệt NCC (1→6) CEO chốt đã xong · GĐ2: Đề xuất Ký kết hợp đồng (7→12) — Có 1 bước email thì bỏ đi cũng đc, thay bằng bước bắt các phiếu từ Giai đoạn 1 → Để nối tiếp làm tiếp. Đây là 1 đầu phiếu mới hoàn toàn · GĐ3: 12→18 (Duyệt Hợp đồng — Cái này mình đã có làm trước rồi, tận dụng lại) · GĐ4 (Bước còn lại): Ký cứng hợp đồng các thứ → có thể nằm ngoài do vướng chữ ký cứng, ký nháy các thứ, có thể ko cần đủ các bước chỉ cần cho upload các file đã ký cứng lên là đc. Đọc lại nắm đủ nội dung. Xong báo lại tao trước khi làm."*
**4 câu chốt (AskUser cùng ngày):**
- **❶ Ranh GĐ:** *"Đúng như em map"* ⇒ GĐ2 = b.7→**12** (b.12 = chốt con số) · GĐ3 = b.**13**→18 · GĐ4 = b.19→21 + ký cứng upload · **tên phiếu/menu GĐ2 = "Đề xuất ký kết hợp đồng"** (spec đổi nhãn theo).
- **❷ Tổng quan:** *"Pipeline 4 GĐ đầy đủ"* — GĐ1+GĐ3 data thật · GĐ2/GĐ4 khung "sắp triển khai".
- **❸ Ẩn 3 nhóm (verbatim):** *"Chỉ nên hiển thị ở trang admin là đc → Để tao khỏi nhầm giữa cái đã public và cái đang làm"* ⇒ `IsVisible=0` cho 3 root `Hrm``Off``Personal` (fe-admin thấy đủ, eOffice ẩn — đúng comment entity `MenuItem.cs`).
- **❹ Mở HĐ + DANH MỤC (verbatim):** *"Hiện đang phát triển → Nên cứ cho hiển thị hết để mọi người góp ý, còn duyệt NCC → thì cứ để như cũ nhé. Phần đó đã tốt rồi."*
🔴 **Ghi sổ rủi ro CÓ Ý THỨC:** lead đã nêu lỗ hổng authz HĐ (`ContractsController``[Authorize]` trần 22 endpoint ghi + Reject-trước-guard, @S156) và rằng mở menu = phơi lỗ cho toàn bộ NV. **Anh tái khẳng định hiển thị hết, KHÔNG vá đợt này** (giữ "chỗ hợp đồng cứ từ từ"). Đây là quyết định chủ dự án, đã cảnh báo, chấp nhận trong giai đoạn phát triển. **PE (Duyệt NCC) KHÔNG chạm.**
## Ánh xạ 4 GĐ ↔ đĩa (đã báo, anh xác nhận)
| GĐ | Bước | Đĩa |
|---|---|---|
| 1 Duyệt NCC | 1→6 | ✅ module PE (`DaDuyet=7`) |
| 2 Đề xuất ký kết HĐ | 7→12 | 📋 spec KHKK 5-wave (nay đổi nhãn); b.7 email **BỎ** — thay bằng tạo-phiếu-từ-PE-`DaDuyet` = **spec W2 đã đúng sẵn** ⇒ Q8 (email external) **ĐÓNG: out-of-scope** |
· 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 ý ❸)
· Thuần cộng: chỉ thêm `CanRead=1, CanCreate/Update/Delete=0` — không rút quyền ai
- [x]**T3 — Spec đổi nhãn** ✅ 2 site (`:26` owner-block + `:222` seed label) — "Đề xuất ký kết hợp đồng"; tên English `ContractSigningPlan*` giữ; ranh GĐ + Q8-ĐÓNG ghi vào spec
- Section hiện có: (a) "Của tôi" = grid 5 `StatCard` (local component, `card-accent` + `icon-chip` + `stat-value`, tone map `STAT_TONE` 6 tone dùng CSS var `--color-*`); (b) "HĐ gần đây" list 5; (c) "Phiếu Duyệt NCC gần đây" list 5 (có `PeFinalizeChip` + PE display-status pill).
2.**`pendingMe` KHÔNG phải query-param của `GET /purchase-evaluations`** — nó là **URL-param của trang list**, và trang list map nó sang **endpoint KHÁC**: `GET /purchase-evaluations/inbox` trả **mảng phẳng**`PeListItem[]` (không paging) — `PurchaseEvaluationsListPage.tsx:81-89` + `PurchaseEvaluationsController.cs:30-35`.
⇒ Đếm "chờ tôi duyệt" = `inbox.length`, KHÔNG dùng được `pageSize=1`.
✅ `phase` THÌ ĐÚNG là query-param BE (`PurchaseEvaluationsController.cs:23``PurchaseEvaluationPhase? phase`), `phase=7` = Đã duyệt.
### Nguồn số cho 4 GĐ (chốt) — chỉ **+2 request**, dưới trần ~3
| Ô số | Nguồn | Request thêm? |
|---|---|---|
| GĐ1 · Tổng phiếu | `total` của query **đã có**`['my-pe-recent']` (`/purchase-evaluations?page=1&pageSize=5`) | ❌ 0 (tái dùng) |
Icon: đã `node -e` verify 18 export lucide dùng tới (`Workflow`, `ClipboardCheck`, `FileSignature`, `Stamp`, `Hourglass`, `ChevronRight/Down`, `RefreshCw`, `ShieldCheck`…) — tránh bẫy alias-dodge.
Route đã verify tồn tại trong `App.tsx`: `/purchase-evaluations`, `/my-contracts`, `/inbox`, `/contracts/new`, `/purchase-evaluations/:id` — mọi link đều trỏ route THẬT (không đâm `*` fallback).
## 3 — Quyết định design (trước khi code)
- **Header:** title → **"Tổng quan tất cả luồng duyệt"**; lời chào `Xin chào, {tên}` KHÔNG vứt — dời xuống `description` cùng câu định vị 4 GĐ.
- **Hero = dải pipeline 4 GĐ:** grid `lg:grid-cols-[1fr_auto_1fr_auto_1fr_auto_1fr]` — 3 ô `auto` là mũi tên nối (`ChevronRight` ở lg, `ChevronDown` khi dọc <lg),`aria-hidden`.Mobile =1cộtdọc,mũitênxuống.
`git status`xácnhận**KHÔNG file `fe-admin/**` nào bị chạm** (các file khác đang M là của lead: WAL/STATUS/HANDOFF/scripts…).
Cấu trúc mới (trên → dưới):
1. `PageHeader` — title **"Tổng quan tất cả luồng duyệt"**, description có lời chào + câu định vị.
2. **HERO pipeline 4 GĐ** (`<section>` bo góc, viền, shadow) — header có icon `Workflow` + eyebrow + "Pipeline 4 giai đoạn · 21 bước"; banner lỗi + nút **Thử lại** (chỉ hiện khi có query fail); dải 4 `StageCard` + 3 `StageArrow`.
3. **"Việc của tôi"** — 5 `StatCard` cũ nguyên vẹn (thêm ghi chú "Số liệu hợp đồng của riêng bạn (giai đoạn 3)" cho khớp sự thật BE).
4. **2 danh sách gần đây** trong `grid gap-4 xl:grid-cols-2` (HĐ gần đây | Phiếu Duyệt NCC gần đây) — thêm nhánh **isError + nút Thử lại** cho cả 2 (trước đây lỗi API = list rỗng câm).
Component mới (local, không đẻ file mới, không dep mới): `StageCard` · `StageArrow` · `StageStatRow` · `StageLink` · `StageSoon` · `SectionTitle` + map `STAGE_TONE`.
Nội dung 4 GĐ đúng nguyên văn owner: GĐ1 "Duyệt NCC" (Bước 1 → 6 · CEO chốt) · GĐ2 "Đề xuất ký kết hợp đồng" (Bước 7 → 12) — *"Phiếu nối tiếp từ phiếu Duyệt NCC đã duyệt — chốt giá trị ký kết."* · GĐ3 "Duyệt Hợp đồng" (Bước 13 → 18) · GĐ4 "Ký cứng & phát hành" (Bước 19 → 21) — *"Upload bản hợp đồng đã ký cứng."*
## 5 — Build (bằng chứng thay screenshot #1)
`cd fe-user && npm run build` (script = `tsc -b && vite build`) — **PASS**. Tail lần chạy cuối:
3 warning (thứ tự `@import` font, chunk >500 kB, INEFFECTIVE_DYNAMIC_IMPORT) đều **có sẵn từ trước**, không sinh từ file này.
## 6 — 🔴 NO-SCREENSHOT (khai theo lệnh) + verify tĩnh thay thế
**KHAI: `NO-SCREENSHOT`.** Môi trường spawn này không có dev-server/Playwright ⇒ **KHÔNG chạy được FD2 visual loop, KHÔNG có ảnh, KHÔNG bịa ảnh.** Thay bằng 2 lớp kiểm tra chạy được:
**(a) Soi CSS đã build** — Tailwind v4 chỉ sinh class nó quét thấy; class không sinh = "im lặng mất màu" (bẫy cũ của repo với stop `-800` của teal/violet). Grep file `dist/assets/*.css` vừa build:
| lưới pipeline `lg:grid-cols-[1fr_auto_1fr_auto_1fr_auto_1fr]` | ✓ → `grid-template-columns:1fr auto 1fr auto 1fr auto 1fr` (1 hit, trong media query lg) |
⇒ **Chỉ dùng stop màu CÓ THẬT** trong `@theme` (teal/violet/amberx/greenx: 50/100/500/600/700 — KHÔNG -800) và mọi màu chữ đè lên rule unlayered (`h1..h4`, `.stat-value`) đều có dấu `!`, đã xác nhận `!important` nằm trong CSS.
**(b) Verify icon trước khi build** — `node -e` liệt 18 export `lucide-react` (Workflow/ClipboardCheck/FileSignature/Stamp/Hourglass/ChevronRight/ChevronDown/RefreshCw/ArrowRight…) → tất cả `object`, không dính bẫy alias.
## 7 — Tự-phê tĩnh theo rubric (FD4) — thẳng, không "trông ổn"
| Tiêu chí | Verdict | Căn cứ / điểm còn nợ |
|---|---|---|
| **Hierarchy** | PASS | 1 focal point = dải pipeline (khối duy nhất có viền + shadow + nền trắng nổi trên nền `#f8fafc`); KPI cá nhân xuống hạng 2; list xuống hạng 3. LIVE ⟂ SOON phân biệt bằng 3 tín hiệu cùng chiều (nền trắng vs xám, rail màu vs nét đứt, có số vs badge). |
| **Spacing/rhythm** | PASS | Toàn bộ giá trị nằm trên thang Tailwind (p-3.5/p-4, mt-1/2/3, gap-1.5/2/3/4) — 0 magic number. **Vá lỗi cũ:** 2 section list trước đây dính sát nhau (section thứ 2 không có margin) → nay `grid gap-4`. |
| **Typography** | PASS | Không thêm size lạ: 10px badge / 11px nhãn-phụ / 13-14px thân / 15px tiêu-đề-GĐ / `text-3xl` KPI. Nhãn có `leading-tight` để tiếng Việt 2 dòng không dính. |
| **Color/contrast** | PASS (1 nợ ghi rõ) | brand-600 `#1F7DC1` là accent chính, KHÔNG default-blue. Đo: `text-slate-600` 7.0:1 · `text-slate-500` 4.8:1 · `text-brand-700` 5.6:1 · `text-brand-800` ~8:1 · `text-teal-700` ~5.6:1 · `text-accent-600` ~7.4:1 — đều ≥ AA. **Sửa 2 chỗ hụt AA:** `hint` của StatCard `slate-400`(2.5:1) → `slate-500`; dấu "—" lỗi `slate-400` → `slate-500`. **Nợ (KHÔNG tự sửa):** class dùng chung `.label-eyebrow` (brand-600 trên trắng ≈ **4.42:1**, hụt 4.5) — đây là token toàn app ở `index.css`, sửa = ảnh hưởng nhiều trang + đụng file agent khác ⇒ trang này **né**, dùng `text-brand-700` cục bộ. Đề xuất lead cân nhắc đổi `.label-eyebrow` → brand-700 ở một đợt riêng. |
| **Polish/states** | PASS | Mỗi ô số có đủ 3 trạng thái: loading = thanh `animate-pulse`, error = "—" + `title`, có số = số thật. Có banner lỗi cấp trang + **Thử lại chỉ refetch query nào fail**. 2 list: skeleton / **error + Thử lại (mới)** / empty / data. Hover + `focus-visible:ring-2` trên MỌI phần bấm được. Motion: chỉ dùng `.card-accent` (đã có `prefers-reduced-motion` trong index.css) + `transition` màu — không thêm animation nào cần chặn. |
| **Responsive** | PASS (theo suy luận tĩnh, chưa soi mắt) | `<lg` = 1 cột dọc, mũi tên `ChevronDown` chen giữa (đúng yêu cầu owner). `lg` = lưới 7 cột `1frauto×3`. Ở 1280px (trừ sidebar ~240 + p-6) mỗi thẻ ≈ 216px: hàng badge+chip ≈ 122px → vừa; nhãn "Chờ tôi duyệt" 11px ≈ 72px + số ≈ 20px trong 190px → vừa 1 dòng, và nếu xuống dòng vẫn an toàn nhờ `leading-tight` + `min-w-0`. `h-full` + `items-stretch` giữ 4 thẻ bằng chiều cao dù tiêu đề GĐ2 dài 2 dòng. Không có `overflow-x` (không đặt min-width cứng). |
| **Detail** | PASS | `tabular-nums` cho mọi số → cột phải thẳng hàng. Không lồng `<button>` trong `<button>` (thẻ GĐ là `<div>`, phần bấm là button con) — HTML hợp lệ, không "nuốt" focus. Ring của row trong list dùng `ring-inset` vì `<section>` có `overflow-hidden` (ring outset sẽ bị cắt). |
| **Anti-slop (FD3)** | PASS | 0 emoji trong UI (dùng lucide) · 0 gradient loè (chỉ giữ gradient brand nhạt sẵn có ở header list + rail) · GĐ chưa làm KHÔNG dùng đỏ/vàng cảnh báo (xám nét đứt = "kế hoạch", không phải "lỗi") · KHÔNG bịa số cho GĐ2/GĐ4. |
**Điểm KHÔNG dám khai:** chưa có mắt người/ảnh nào nhìn trang này. Hai thứ tôi muốn soi khi có prod: (i) chiều cao dải pipeline ở tablet 768–1023px (4 thẻ xếp dọc ≈ 630px, có thể muốn thu gọn), (ii) khi `Đã quá hạn > 0` số đỏ trong thẻ teal có chỏi không.
## 8 — Bàn giao / lưu ý cho lead
- **KHÔNG commit** (đúng luật: lead commit, scope `FE-User`). **KHÔNG chạm BE**, **không thêm dependency**, **không sửa `index.css`** (file đang có agent khác đụng).
- 2 request thêm đúng như thiết kế: `['pipeline-pe-approved']` và `['pipeline-pe-inbox']` — queryKey mới, KHÔNG đụng cache của trang list (`['pe-list', …]`).
- Nếu sau này GĐ2/GĐ4 có module thật: chỉ cần truyền `tone` + đổi `<StageSoon>` thành mấy `<StageStatRow>` — khung đã chừa sẵn, không phải đập lại.
- Khi deploy prod: đây là trang duy nhất cần soi mắt lại (route `/dashboard`, login user thường). Rig dev authed vẫn vướng gotcha #3 (API HTTPS :5443 ⟂ vite proxy HTTP) — verify bằng prod là đường ngắn nhất.
<!-- END sub-frontend-designer-tongquan · TOTAL=8 muc -->
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.