Files
solution-erp/.claude/workflows/runs/2026-08-05-S174-khkk-style-audit/sub-frontend-designer-L3.md
2026-08-05 16:27:07 +07:00

30 KiB
Raw Blame History

sub-frontend-designer-L3 — LĂNG-KÍNH: HOST / BỐ CỤC

Run: 2026-08-05-S174-khkk-style-audit · Lane L3 (ensemble /fable-clone frontend-designer, 5 lane cùng vai) Mục tiêu: quét style/UX host+bố-cục của KHKK (GĐ2) đối chiếu Duyệt NCC (GĐ1, golive = CHUẨN). Đầu ra: spec thi-hành-được cho Opus 5 MAX.

0. KHUNG RỖNG (ghi @lượt 1, TRƯỚC khi đọc file) — append-as-I-read

0.1 File trong lăng-kính (3 chính)

  • A. fe-admin/src/pages/pe/PurchaseEvaluationWorkspacePage.tsx — CHUẨN (host 2-panel "Thao tác" GĐ1) — đọc HẾT 147 dòng
  • B. fe-admin/src/pages/khkk/KhkkCreatePage.tsx — màn "Thao tác" GĐ2, vừa dựng lại — đọc HẾT 394 dòng
  • C. fe-admin/src/pages/khkk/KhkkListPage.tsx — màn "Danh sách" GĐ2 (3-panel) — đọc HẾT 206 dòng
  • (đối chiếu) fe-admin/src/pages/pe/PurchaseEvaluationsListPage.tsxKHÔNG mở toàn file (807 dòng): chỉ grep có mục tiêu + đọc đúng 24 dòng header/grid (:379-402)
  • (phụ, grep-only — không mở toàn file) components/pe/PeListPanel.tsx · components/khkk/KhkkWorkListPanel.tsx · components/Layout.tsx · components/TopBar.tsx · src/index.css

0.2 Trục so sánh (6 trục bắt buộc)

  • T1. Header trang: icon · cỡ chữ title · badge · dòng hint bên phải → §1/§2/§3, lệch: badge (H-3)
  • T2. Chiều cao host h-[calc(100vh-4rem)]KHỚP 4/4 file; sai ~3px là lỗi toàn hệ (§3bis-d) — KHÔNG sửa
  • T3. Grid cột + breakpoint → LỆCH 300/340 vs 260/320 (H-1), số học ở §3bis-b
  • T4. EmptyState → KHỚP khuôn (icon+title+description), KHKK còn thêm nhánh not-found mà PE thiếu
  • T5. Hành vi <lgLỆCH NẶNG: KHKK-Create không có gì ⇒ ngõ cụt (H-2), 3 khuôn khác nhau ở §3bis-e
  • T6. Cách truyền readOnlyLỆCH: hằng số (Create) vs công thức host (List) (H-4)

0.3 Bảng kết

# Trục PE (chuẩn) KHKK Create KHKK List Verdict Sửa
T1a shell + header class flex h-[calc(100vh-4rem)] flex-col + header 1 khuôn y hệt (:168-169) y hệt (:123-124) KHỚP BYTE
T1b icon ClipboardCheck h-5 w-5 text-slate-500 FileCheck cùng size/màu FileCheck (đổi icon = đúng, theo module)
T1c title text-base font-semibold tracking-tight text-slate-900, không truncate + min-w-0+truncate + min-w-0+truncate ⚠️ KHKK tốt hơn H-7 giữ
T1d badge Workspace: KHÔNG · List: pill số (:386) KHÔNG có pill "Giai đoạn 2 · nhãn" (:128) 🔴 lệch nội-bộ GĐ2 H-3
T1e phải header hint text-[12px] text-slate-500 · List: TRỐNG hint Button "Lập kế hoạch" có lý do H-9 giữ
T2 chiều cao 100vh-4rem y hệt y hệt
T3 grid [260px_1fr] / xl [320px_1fr] [300px_1fr] / xl [340px_1fr] [400px_1fr_360px] = PE list 🔴 Create lệch H-1
T4 EmptyState icon+title+desc (:102-108) icon+title+desc (:217-221) + rẽ deletedView (:163-171)
T5 <lg dead-code (:135-144) không có gì ⇒ không submit được matchMedia → navigate (:102-109) 🔴 hỏng chức năng H-2
T6 readOnly không dùng — mode="workspace" readOnly hằng số (:210) công thức host (:112-117) 🔴 2 luật cho 1 component H-4
mirror 2 app SHA256 == fe-user SHA256 == fe-user 4/4 SAME giữ khi sửa

0.4 Spec thi-hành-được → §4 (H-1…H-9, mỗi mục: file:line · hiện-trạng · đích · vì · rủi-ro)

  • Đã viết. Thứ tự thi hành + danh sách KHÔNG SỬA ở cuối §4.

0.5 Nhật ký đọc (theo thứ tự tool-call)

  1. wc -l 4 file + Read B KhkkCreatePage (394) — song song
  2. Read A PurchaseEvaluationWorkspacePage (147) + Read C KhkkListPage (206) — song song → APPEND §1/§2/§3
  3. Grep header/grid PurchaseEvaluationsListPage + Grep root-className 3 panel
  4. Grep KhkkWorkListPanel (sticky/tier) + Grep PeListPanel (sticky create)
  5. Grep PurchaseEvaluationsListPage cơ-chế <lg (overlay expand=1) + Bash sha256 mirror + git status
  6. Bash grep -c Button (đối chứng) + Grep Layout.tsx (main/topbar)
  7. Grep TopBar.tsx (h-14) + Bash khảo sát toàn bộ lg:grid-cols-[ → APPEND §3bis + §4
  8. Bash index.css tokens + probe cổng + Read PurchaseEvaluationsListPage:379-402
  9. Bash đếm stop violet-* toàn repo → sửa lại H-8 (bác giả-thuyết từ MEMORY) + APPEND §5

1. FILE A — PurchaseEvaluationWorkspacePage.tsx (CHUẨN) — 147 dòng, ĐÃ ĐỌC HẾT

Khuôn host "Thao tác" GĐ1 (đã golive, owner tuyên "đã tốt rồi", byte revert 1b3bcaa):

Chi tiết Giá trị Dòng
Shell <div className="flex h-[calc(100vh-4rem)] flex-col"> :69
Header flex shrink-0 flex-wrap items-center justify-between gap-3 border-b border-slate-200 bg-white px-6 py-3 :70
Icon ClipboardCheck h-5 w-5 text-slate-500 :72
Title text-base font-semibold tracking-tight text-slate-900 — KHÔNG truncate, KHÔNG min-w-0 ở wrapper (:71 flex items-center gap-2) :71-73
Badge KHÔNG CÓ badge ở host Thao-tác
Hint phải text-[12px] text-slate-500 — "Workspace 2-panel — Workflow + Duyệt ở menu “Duyệt”." :75-77
Grid grid flex-1 grid-cols-1 overflow-hidden lg:grid-cols-[260px_1fr] xl:grid-cols-[320px_1fr] :80
Panel 2 <main className="hidden overflow-y-auto bg-slate-50 p-6 lg:block"> :100
EmptyState icon={ClipboardCheck} + title "Chọn phiếu hoặc tạo mới" + description có nhắc nút "+ Thêm mới" :102-108
readOnly KHÔNG có prop readOnly — thay bằng mode="workspace" truyền xuống PeDetailTabs (:127) + editableOnly cho panel list (:95) :95,127
<lg Panel 2 ẩn; có khối lg:hidden (:136-144) — DEAD CODE: <button className="hidden"> không bao giờ bấm được, comment nói "tap row khi mobile sẽ navigate fullpage" nhưng KHÔNG ai gọi :135-144
Loading {selectedId && detail.isLoading && <div className="text-sm text-slate-500">Đang tải…</div>} :120-122
Not-found KHÔNG có nhánh "Không tìm thấy phiếu"

⚠️ Ghi nhận NGƯỢC: chuẩn GĐ1 CÓ 2 chỗ yếu (dead-code lg:hidden :136-144 · thiếu nhánh not-found) — KHÔNG được "mirror cho giống" 2 cái này.

2. FILE B — KhkkCreatePage.tsx (màn Thao-tác GĐ2, vừa dựng lại) — 394 dòng, ĐÃ ĐỌC HẾT

Chi tiết Giá trị Dòng vs PE
Shell flex h-[calc(100vh-4rem)] flex-col :168 KHỚP byte
Header class y hệt PE :169 KHỚP byte
Icon FileCheck h-5 w-5 text-slate-500 :171 khớp khuôn (icon khác = đúng, GĐ2 dùng FileCheck xuyên suốt)
Title wrapper flex min-w-0 items-center gap-2 + truncate trên <h1> :170-172 ⚠️ LỆCH nhẹ vs PE (PE không truncate) — nhưng đây là cải tiến ĐÚNG vì title GĐ2 dài (… — Thao tác · <nhãn nhóm>), khớp KhkkListPage:125-127
Badge "Giai đoạn 2" THIẾUKhkkListPage:128-130 CÓ pill Giai đoạn 2 · <nhãn nhóm>, màn Thao-tác KHÔNG :169-177 🔴 LỆCH NỘI-BỘ GĐ2 (2 màn cùng module, 1 có 1 không)
Hint phải text-[12px] text-slate-500 "Bấm phiếu ở cột trái — tầng trên lập kế hoạch, tầng dưới chỉ xem." :174-176 KHỚP khuôn PE
Grid lg:grid-cols-[300px_1fr] xl:grid-cols-[340px_1fr] :179 🔴 LỆCH SỐ ĐO: PE = 260px/320px. Comment đầu file (:6) tự khai "bê khuôn lg:grid-cols-[300px_1fr]" — tức là comment mô tả đúng code nhưng code KHÔNG đúng khuôn PE
Panel 2 hidden overflow-y-auto bg-slate-50 p-6 lg:block :187 KHỚP byte
EmptyState icon={FileCheck} + title "Chọn phiếu Duyệt NCC để lập kế hoạch" + desc chỉ rõ 2 tầng :217-221 khớp khuôn
Loading Đang tải… text-sm text-slate-500 (:205) :205
Not-found Không tìm thấy phiếu. text-sm text-red-600 (:207) :207 TỐT HƠN PE
readOnly <KhkkDetailContent plan={…} readOnly onChanged={…} />hard-code readOnly không điều kiện :210 🔴 LỆCH TRỤC: KhkkListPage:113-117 tính readOnly theo phase+drafter (P2-7 "host quyết"); màn Thao-tác ép cứng ⇒ người vừa tạo phiếu KHÔNG sửa được ngay tại chỗ, phải bấm "Mở trang đầy đủ"
<lg Panel 2 hidden … lg:block; KHÔNG có fallback navigate (không selectPlan-style matchMedia) :187 ⚠️ mobile = panel trái nằm 1 mình, bấm PE ⇒ set state nhưng panel 2 vẫn ẩn ⇒ bấm không thấy gì. KhkkListPage:102-109matchMedia('(min-width:1024px)') → navigate /khkk/:id. PE cũng hở (dead-code) nhưng ở PE panel 1 có nút "+ Thêm mới" nên ít chết hơn
Banner "đang xem" border-violet-200 bg-violet-50 + 2 nút (Mở trang đầy đủ outline · Về form tạo ghost) :191-204 KHÔNG có ở PE — hợp lý (PE không có chế-độ xem-phiếu-người-khác trong workspace). Màu violet = token ngoài dải brand/slate của host (host GĐ1 chỉ dùng slate/brand)
Form card rounded-lg border border-slate-200 bg-white shadow-sm + divide-y divide-slate-200 + Section px-5 py-4 :226-236,387-393 khớp PeWorkspaceCreateView (helper Section khai rõ là duplicate cố ý, :385-386)
Footer form border-t border-slate-200 bg-slate-50 px-5 py-3, hint trái + nút phải :353-376 khuôn PE-create
Ký hiệu cảnh báo dùng ký tự / trong chuỗi text (:324, :357, :360) :324,357,360 🔴 ANTI-SLOP FD3: glyph thay icon. PE-family dùng lucide (X, Plus, ClipboardCheck). GĐ2 List cũng dùng lucide X (:189)

3. FILE C — KhkkListPage.tsx (màn Danh-sách GĐ2, host 3-panel) — 206 dòng, ĐÃ ĐỌC HẾT

Chi tiết Giá trị Dòng
Shell flex h-[calc(100vh-4rem)] flex-col :123
Header class y hệt PE/Create :124
Icon FileCheck h-5 w-5 text-slate-500 :126
Title truncate text-base font-semibold tracking-tight text-slate-900, wrapper flex min-w-0 items-center gap-2 :125-127
Badge ml-2 truncate rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-medium text-slate-600 — "Giai đoạn 2 · <nhãn nhóm>" :128-130
Phải Nút Lập kế hoạch (Plus + navigate /khkk/create?group=) — KHÔNG phải dòng hint :132-137
Grid lg:grid-cols-[400px_1fr_360px] (3 panel) :140
Panel 2 hidden overflow-y-auto bg-slate-50 p-6 lg:block :162
Panel 3 hidden overflow-y-auto border-l border-slate-200 bg-white p-4 lg:block :186
EmptyState icon={FileCheck}, title/desc rẽ theo deletedView :163-171
<lg selectPlan :102-109 dùng window.matchMedia('(min-width: 1024px)')<lg navigate /khkk/:id (giữ ?group=) :102-109
readOnly tính ở HOST: `!plan
Hộp rỗng panel 3 rounded-lg border border-dashed border-slate-200 p-6 text-center text-sm text-slate-400 + lucide X :188-193

3bis. BẰNG CHỨNG PHỤ (đọc ngoài 3 file chính — chỉ grep có mục tiêu, KHÔNG mở toàn file)

(a) Khảo sát TOÀN BỘ host-grid fe-admin/src/pages (grep -rn "lg:grid-cols-\[")

contracts/ContractCreatePage.tsx:129        lg:grid-cols-[320px_1fr]                 ← "thao tác" GĐ3
contracts/ContractsListPage.tsx:125         lg:grid-cols-[340px_1fr_360px]
khkk/KhkkCreatePage.tsx:179                 lg:grid-cols-[300px_1fr] xl:[340px_1fr]  ← GĐ2 thao tác
khkk/KhkkListPage.tsx:140                   lg:grid-cols-[400px_1fr_360px]           ← = PE list ✅
pe/PurchaseEvaluationsListPage.tsx:397      lg:grid-cols-[400px_1fr_360px]           ← CHUẨN list
pe/PurchaseEvaluationWorkspacePage.tsx:80   lg:grid-cols-[260px_1fr] xl:[320px_1fr]  ← CHUẨN thao tác

KhkkCreatePagegiá trị THỨ BA trong họ "thao tác" (260/320 · 320 · 300/340) — không khớp PE, cũng không khớp Contract. ⇒ Đối chứng DƯƠNG: KhkkListPage:140 khớp PurchaseEvaluationsListPage:397 byte-for-byte, và KhkkListPage:9 ghi thẳng luật "Số đo panel lấy của PE (400px) vì hiến-pháp owner là 'giống nhất với Duyệt NCC'"luật đã tồn tại, file Create không áp.

(b) Số đo thật (Layout fe-admin/src/components/Layout.tsx:372 sidebar w-72 = 288px, xl:w-80 = 320px)

Viewport Vùng nội dung Panel-1 PE Panel-2 PE Panel-1 KHKK-Create Panel-2 KHKK-Create Δ panel-2
1024 (lg) 1024288 = 736 260 476 300 436 40px
1280 (xl) 1280320 = 960 320 640 340 620 20px
1366 (laptop SE) 1366320 = 1046 320 726 340 706 20px
Panel-2 KHKK-Create trừ p-6 (48px) ⇒ 388px ở lg. Trong đó Section 2 dùng md:grid-cols-2 (:284) — md:viewport-based (768px), KHÔNG container-based ⇒ ở 1024 nó ĐÃ chia đôi: 2 ô ~186px cho "Nhóm duyệt" + "Quy trình duyệt" (ô quy trình in mã · têntruncate, :313-317) ⇒ tên quy trình gần như chắc chắn bị cắt. PE cho 428px ⇒ ~210px/ô.
⇒ Lệch đi NGƯỢC hướng nhu cầu: cột danh sách rộng hơn chuẩn 40px, cột FORM (thứ cần chỗ) hẹp hơn chuẩn 40px.

(c) Panel-1: hợp đồng bố-cục

  • PeListPanel.tsx:92 root <aside className="flex flex-col overflow-hidden border-r border-slate-200 bg-white"> + sticky bottom action :238-241 <div className="border-t border-slate-200 bg-white p-2"><Button … (grep -c Button = 8).
  • KhkkWorkListPanel.tsx:142 root <aside className="flex min-h-0 flex-col overflow-hidden border-r border-slate-200 bg-white"> — khớp + min-h-0 (cần cho 2 tầng). Nhưng grep -c Button = 0KHÔNG có nút hành-động dính đáy. 🔴 ⇒ comment KhkkCreatePage.tsx:7 "Panel 1 = danh sách (KhkkWorkListPanel) + nút hành-động dính đáy" MÔ TẢ THỨ KHÔNG TỒN TẠI (claim > việc đã làm). Thứ ở đáy tầng-1 là :219 <div className="shrink-0 border-t-2 border-slate-200" /> = vạch ngăn 2 tầng, không phải nút.
  • 2 tầng: Tier() :48-73, dùng ở :154 và :221; min-h-0 flex-1 cả hai ⇒ tỉ lệ 50/50 (comment :46-47).

(d) Chiều cao h-[calc(100vh-4rem)] — đo lại chrome thật

Layout.tsx:370 dải gradient h-1 (4px) + TopBar.tsx:84 <header className="flex h-14 …"> (56px) + border-b (1px) = 61px ≈ 3.81rem, trong khi mọi host trừ 4rem = 64px ⇒ hụt ~3px. ⇒ KHÔNG phải lỗi KHKK — 4/4 host (PE ×2, KHKK ×2) đều h-[calc(100vh-4rem)], sai lệch là toàn hệ thống. Ghi nhận, KHÔNG đưa vào spec sửa lần này (đổi 1 file = lệch khỏi 3 file kia; phải sửa cả họ hoặc không sửa).

(e) <lg — 3 khuôn KHÁC NHAU

Màn Cơ chế Bằng chứng
PE list (chuẩn 3-panel) selectRow set expand=1 khi window.innerWidth < 1024 ⇒ overlay full-bleed PurchaseEvaluationsListPage.tsx:147-158
KHKK list matchMedia('(min-width: 1024px)') sai ⇒ navigate('/khkk/:id') KhkkListPage.tsx:102-109
PE workspace (chuẩn 2-panel) dead-code lg:hidden + <button className="hidden"> không ai bấm PurchaseEvaluationWorkspacePage.tsx:135-144
KHKK Create KHÔNG CÓ GÌ KhkkCreatePage.tsx:187 hidden … lg:block
🔴 Hệ quả KHKK-Create ở <lg: grid về grid-cols-1 ⇒ chỉ KhkkWorkListPanel hiện (aside không có lg: gate). Bấm PE ⇒ pickPe set state (:153-156) nhưng <main> vẫn hiddenmàn không đổi gì. Form + nút "Tạo kế hoạch" (:372) nằm TRONG <main>không thể tạo kế hoạch trên màn <1024px. Đây là hỏng chức năng, không phải lệch thẩm mỹ — nặng hơn dead-code của PE (PE vẫn còn nút "+ Thêm mới" ở panel-1 nhưng cũng dẫn vào <main> ẩn ⇒ PE cũng hở, chỉ là hở SẴN của chuẩn).

(f) Mirror 2 app — SẠCH

sha256sum fe-admin vs fe-user (4 file KHKK): KhkkCreatePage bac8e0050711 · KhkkListPage ee0dc8afc5fd · KhkkWorkListPanel fcb97650f51c · KhkkListPanel ad9073db2c90SAME 4/4. git status --porcelain -- fe-admin/src fe-user/src = rỗng ⇒ bản S174 đã commit, không có drift treo. ⚠️ Mọi sửa dưới đây PHẢI áp CẢ 2 app rồi so lại SHA256.


4. SPEC THI-HÀNH-ĐƯỢC (cho Opus 5 MAX) — lăng-kính HOST/BỐ-CỤC

Nguyên tắc chấm: chuẩn = PurchaseEvaluationWorkspacePage.tsx (GĐ1 golive). Chỉ lệch khi có lý do viết ra được; lệch không lý do ⇒ kéo về chuẩn. Mọi patch áp CẢ fe-admin/fe-user/ rồi verify SHA256 == (§3bis-f).

H-1 🔴 MAJOR — Grid cột sai chuẩn (KhkkCreatePage.tsx:179)

  • Hiện: lg:grid-cols-[300px_1fr] xl:grid-cols-[340px_1fr]
  • Đích : lg:grid-cols-[260px_1fr] xl:grid-cols-[320px_1fr] (byte-identical PurchaseEvaluationWorkspacePage.tsx:80)
  • Vì : (i) hiến-pháp owner "giống Duyệt NCC" — file anh em KhkkListPage.tsx:9 đã áp đúng luật này cho 400px; (ii) §3bis-b: đang lấy 40px của cột form đưa cho cột danh sách, ngược nhu cầu.
  • Kèm : sửa luôn comment :6 (lg:grid-cols-[300px_1fr]) — comment đang mô tả số cũ, để lại là bẫy cho lượt sau.
  • Rủi ro: panel-1 hẹp đi 40px ⇒ dòng PE trong Tier có thể wrap. Kiểm bằng mắt tầng-1 ở 1024 & 1366; nếu wrap thì KHÔNG nới grid mà rút gọn nội dung dòng (nguyên tắc: giữ khuôn host, đổi nội dung panel).

H-2 🔴 MAJOR — <lg là ngõ cụt, không tạo được phiếu (KhkkCreatePage.tsx:187)

  • Hiện: <main className="hidden … lg:block">, không có nhánh nào cho <lg.
  • Đích : theo khuôn KhkkListPage.tsx:102-109 (cùng module, đã chạy): trong pickPe, nếu !window.matchMedia('(min-width: 1024px)').matchesnavigate('/khkk/create-mobile'…) KHÔNG có ⇒ cách rẻ + đúng nhất: bỏ hidden … lg:block cho <main>, để nó xếp DƯỚI panel-1 ở <lg (grid-cols-1 sẵn có ⇒ 2 hàng), và thêm lg: cho panel-1 nếu cần giới hạn chiều cao.
  • ⚠️ KHÔNG mirror dead-code PurchaseEvaluationWorkspacePage.tsx:135-144 — nó là nợ, không phải chuẩn.
  • Vì : nút submit nằm trong <main> ⇒ hiện tại <1024px = không dùng được màn. Đây là lỗi chức năng.
  • Cách rẻ hơn nếu owner không muốn đụng layout mobile: giữ hidden lg:block NHƯNG thêm khối lg:hidden nói thẳng "Màn Thao tác cần màn hình ≥1024px — mở mục Danh sách để xem/tạo" + nút navigate /khkk/list. Thà nói thật còn hơn im lặng.

H-3 🟡 MEDIUM — Thiếu badge "Giai đoạn 2" ở header màn Thao-tác (KhkkCreatePage.tsx:169-177)

  • Hiện: chỉ icon + title.
  • Đích : chèn sau <h1> đúng span của KhkkListPage.tsx:128-130: <span className="ml-2 truncate rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-medium text-slate-600">Giai đoạn 2{presetGroup !== null ? · ${khkkGroupMenuLabel(presetGroup)} : ''}</span>
  • Vì : 2 màn CÙNG module hiện KHÁC nhau. Chuẩn PE cũng có badge ở header list (PurchaseEvaluationsListPage.tsx:386, nội dung là số lượng) ⇒ badge-ở-header là ngôn ngữ chung của họ; GĐ2 đã chọn nghĩa "giai đoạn/nhóm" thì phải nhất quán 2 màn.
  • Kèm : nếu chèn badge thì title đã có khkkGroupMenuLabel (:164) ⇒ bỏ phần · <nhãn nhóm> khỏi title để không lặp 2 lần cùng một chữ trên 1 dòng (title về Kế hoạch ký kết HĐ — Thao tác, nhãn nhóm sống trong badge — đúng như màn List).

H-4 🟡 MEDIUM — readOnly ép cứng, không do host tính (KhkkCreatePage.tsx:210)

  • Hiện: <KhkkDetailContent plan={viewPlan.data} readOnly … /> — hằng số.
  • Đích : dùng CÙNG công thức host của KhkkListPage.tsx:112-117 (!plan || !(phase ∈ {DangSoanThao, TraLai}) || plan.drafterUserId !== user.id), tức import useAuth như file anh em.
  • Vì : (i) trục "cách truyền readOnly" đang có 2 luật khác nhau cho cùng 1 component ⇒ người tạo xong phiếu ở màn Thao-tác không sửa được ngay, phải bấm "Mở trang đầy đủ" (:196) — thao tác thừa do host, không do quyền; (ii) [P2-7] đã chốt "readOnly do HOST quyết".
  • ⚠️ NẾU owner CỐ Ý muốn màn Thao-tác luôn chỉ-xem (banner :193 nói vậy) ⇒ giữ nguyên code, nhưng ghi lý do vào comment ngay tại :210 — hiện chỗ này không có dòng nào giải thích, nên đọc như bug.

H-5 🟡 MEDIUM — Comment sai sự thật (KhkkCreatePage.tsx:7)

  • Hiện: "Panel 1 = danh sách (KhkkWorkListPanel) + nút hành-động dính đáy". Thực tế grep -c Button KhkkWorkListPanel.tsx = 0 (đối chứng dương: PeListPanel.tsx = 8, có sticky bottom :238-241).
  • Đích : hoặc (a) xoá vế "+ nút hành-động dính đáy", hoặc (b) thêm thật nút dính đáy panel-1 theo khuôn PeListPanel.tsx:238-241 (<div className="border-t border-slate-200 bg-white p-2">) — hợp lý nếu muốn có lối "Làm mới danh sách" / "Về form tạo" khi đang xem phiếu ở <lg.
  • Vì : comment là hợp-đồng cho lượt sau; sai comment ⇒ lượt sau tin có nút, không kiểm.

H-6 🟢 MINOR — Glyph thay icon (KhkkCreatePage.tsx:324, 357, 360)

  • Hiện: ⚠ Chưa có quy trình mã… (:324) · ⚠ Chưa đủ để tạo kế hoạch… (:357) · ✓ Đủ thông tin bắt buộc… (:360).
  • Đích : lucide-react<AlertTriangle className="mr-1 inline h-3.5 w-3.5" /> / <Check … />, hoặc bỏ hẳn glyph và để màu chữ (text-red-600 / text-emerald-600) mang nghĩa.
  • Vì : FD3 anti-slop (glyph/emoji thay icon-set); cả họ file dùng lucide (KhkkListPage:30 FileCheck, Plus, X; PurchaseEvaluationsListPage:10 5 icon). Glyph còn render lệch baseline giữa font Be Vietnam Pro và fallback emoji-font.

H-7 🟢 MINOR — Title truncate + min-w-0: GIỮ, và nâng ngược lên PE

  • KhkkCreatePage:170-172 + KhkkListPage:125-127min-w-0 + truncate; PE (:71,73, PurchaseEvaluationsListPage:383,385) KHÔNG.
  • Verdict: KHKK đúng hơn chuẩn (title GĐ2 dài do nhãn nhóm). ⇒ KHÔNG hạ KHKK xuống; đề xuất RIÊNG (ngoài lượt này, cần owner đồng ý vì chạm file golive): thêm min-w-0+truncate cho 2 file PE.

H-8 🟢 MINOR — Màu violet của banner "đang xem" (KhkkCreatePage.tsx:191-204, 288) — ĐÃ ĐO, KHÔNG SỬA

  • Hiện: border-violet-200 bg-violet-50 text-violet-800 (:191-192) + pill nhóm border-violet-200 bg-violet-50 text-violet-700 (:288).
  • Verdict bố-cục: giữ — host GĐ1 không có trạng-thái "đang xem phiếu" nên không có chuẩn để đối chiếu; violet tách "chế-độ xem" khỏi brand (hành-động) và amber (cảnh báo). Chốt 1 luật cho GĐ2: violet = chế-độ chỉ-xem.
  • 🔬 Tự bác bỏ giả-thuyết của chính tao (đo trước khi khuyên): tao vào lượt này mang sẵn luật trong MEMORY "dải accent chỉ có stop 50/100/500/600/700, dùng -800 = silent no-class". ĐO LẠI: SAI cho violet.
    • fe-admin/src/index.css:5 @theme {KHÔNG có directive reset (grep -n "initial" index.css0 hit) ⇒ dải Tailwind mặc định vẫn còn, @theme chỉ ghi đè 5 stop (:41-45 violet 50/100/500/600/700).
    • violet-200/violet-800 VẪN RA MÀU — nhưng là màu của dải mặc định Tailwind (tím #ddd6fe/#5b21b6), khác hệ với dải tự-định-nghĩa (xanh-tím #f1effe/#5042c4). Đây là trộn 2 hệ màu trong 1 khối, KHÔNG phải mất-class.
    • Quy mô: grep -rno "violet-[0-9]+" fe-admin/srcviolet-700×45 · violet-50×38 · violet-200×13 · violet-100×12 · violet-800×11 · violet-500×11 · violet-600×7 · violet-300×4 · violet-400×1, nằm ở 12 file — trong đó có file GĐ1 golive (components/pe/PeDetailTabs.tsx, PeWorkflowPanel.tsx, PeFinalizeChip.tsx).
    • TOÀN HỆ THỐNG, không phải lệch của KHKK. Sửa riêng KhkkCreatePage = tạo lệch mới với chính GĐ1. KHÔNG đưa vào lượt sửa này; nếu owner muốn dọn thì mở việc riêng "chuẩn hoá dải violet 2-hệ" quét cả 12 file × 2 app.
    • 🔴 Luật ĐÚNG (thay luật cũ trong MEMORY): tên màu CÓ trong Tailwind mặc định (teal, violet) mà chỉ định-nghĩa-lại vài stop ⇒ stop ngoài dải im lặng rơi về hệ màu mặc định (nguy hơn: trông vẫn "có màu"). Tên BỊA (amberx, greenx) ⇒ stop ngoài dải không tồn tại class (mất style hẳn). Hai ca KHÁC NHAU, trước nay tao gộp làm một.

H-9 INFO — Nút "Lập kế hoạch" ở header màn List (KhkkListPage.tsx:132-137) — KHÔNG SỬA

  • Chuẩn PurchaseEvaluationsListPage.tsx:382-390: header chỉ có nhóm trái (icon + title + badge-số), bên phải TRỐNG — GĐ1 tạo phiếu bằng sticky "+ Thêm mới" trong PeListPanel:238-241 ở màn Thao-tác.
  • GĐ2 đảo lại: nút tạo nằm ở header màn List, còn panel-1 màn Thao-tác KHÔNG có nút nào (§3bis-c).
  • Verdict: giữ nút ở List (gỡ đi thì GĐ2 mất lối vào duy nhất nhìn thấy được), nhưng nó chính là lý do H-5 phải chọn nhánh (a) sửa comment — đừng thêm nút vào panel-1 chỉ để "giống PE", vì lối tạo của GĐ2 đã có chỗ đứng khác và đang hoạt động.

Thứ tự thi hành đề nghị

H-2 (hỏng chức năng) → H-1 (số đo, 1 dòng) → H-3+H-7-giữ (header) → H-4 (quyết định owner: sửa hay ghi lý do) → H-5, H-6 (dọn). Sau mỗi nhóm: cd fe-admin && npm run build + cd fe-user && npm run build + sha256sum 2 bản == nhau.

KHÔNG SỬA (chốt để lượt sau khỏi "sửa cho giống")

  • h-[calc(100vh-4rem)] hụt ~3px — lỗi toàn hệ thống, sửa lẻ 1 file = tạo lệch mới (§3bis-d).
  • Dead-code lg:hidden của PE (:135-144) — đừng mirror.
  • Thiếu nhánh "Không tìm thấy phiếu" của PE — KHKK đã tốt hơn (KhkkCreatePage:207, KhkkListPage:174), giữ.
  • Grid 3-panel KhkkListPage:140đã khớp PE byte-for-byte, không đụng.

5. TỰ ĐÁNH GIÁ + GIỚI HẠN

5.1 🔴 KHÔNG CÓ SCREENSHOT — khai thẳng, không bịa (khuôn NO-SCREENSHOT S159)

Vòng FD2 (run → shoot → critique) không chạy được trong lượt này:

  • Probe cổng: netstatchỉ 0.0.0.0:8080 LISTENING (pid 5912). 5443/5444 (API) KHÔNG có ⇒ đăng nhập fail.
  • /khkk/create/khkk/list đều sau ProtectedRoute ⇒ không token thì kẹt /login (gotcha #3 đã ghi trong MEMORY: dev-rig chặn authed page).
  • Dựng harness render-isolated cố ý KHÔNG làm: mandate ISOLATION của run này chỉ cho ghi sub-frontend-designer-L3.md; harness = file mới trong fe-user//fe-admin/ ⇒ vi phạm git-diff audit. ⇒ Mọi kết luận dưới đây là đo tĩnh trên mã nguồn + số học layout, KHÔNG phải quan sát bằng mắt. Ba mục cần nghiệm thu bằng mắt trên prod sau khi sửa: H-1 (panel-1 260px có wrap dòng PE không), H-2 (<lg sau khi mở <main>), H-3 (badge + title không tràn ở 1366).

5.2 Độ chắc của từng kết luận

Mục Loại bằng chứng Chắc
H-1 grid so chuỗi class + khảo sát 6 host + số học px CAO — đọc được từ mã, không cần chạy
H-2 <lg hidden … lg:block + vị trí nút submit trong DOM CAO về mặt suy luận DOM · CHƯA nhìn bằng mắt
H-3 badge so 2 file cùng module CAO
H-4 readOnly hằng số vs công thức host CAO (còn phụ thuộc ý owner, đã nêu 2 nhánh)
H-5 comment sai grep -c Button = 0 (đối chứng dương PeListPanel = 8) CAO
H-6 glyph 3 vị trí literal CAO
H-7 truncate so 4 file CAO
H-8 violet đo index.css + đếm toàn repo — bác giả-thuyết ban đầu của chính tao CAO

5.3 Ngoài lăng-kính này (lane khác lo)

Nội dung bên trong panel (KhkkWorkListPanel 2 tầng, KhkkDetailContent, KhkkListPanel cây toàn-trình), micro-copy, trạng thái loading/empty của từng tầng, a11y focus-order, typography scale — KHÔNG chấm ở lane HOST/BỐ-CỤC. Tao chỉ chạm chúng ở mức "root className có khớp hợp-đồng grid của host không".

5.4 Đã KHÔNG làm (giữ lane)

Không sửa 1 dòng code nào (lane audit). Không đụng agent-memory/*, không đụng MD canonical, không đụng sub-MD lane khác. git status --porcelain -- fe-admin/src fe-user/src đo được là rỗng trước khi tao bắt đầu và tao không ghi gì vào đó.

END sub-frontend-designer-L3 — VERDICT=KHỚP-MỘT-PHẦN: 4 trục khớp chuẩn (shell/header-class/panel-2/EmptyState), 2 lệch nặng (grid 300→260 · <lg ngõ cụt không tạo được phiếu), 3 lệch vừa (badge GĐ2 · readOnly ép cứng · comment sai sự thật), 2 minor (glyph thay icon · truncate — giữ vì tốt hơn chuẩn), 3 mục CHỐT-KHÔNG-SỬA (h-calc toàn hệ · violet 2-hệ toàn hệ · nút tạo ở header List). KHÔNG có screenshot — đã khai lý do đo được.