[CLAUDE] FE-Admin: KHKK man nhap — header hang muc du an + doi nut them vao bang (FE-only, 0-mig)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m59s

Anh gui anh man nhap KHKK/2026/008, 3 diem khoanh do -> lam 2, bo 1:

(1) "Lam header duoi day": o HANG MUC (DU AN) doi tu muc 1 xuong lam header
    cua muc 2. No la goi viec ma CA BANG thuoc ve, dung dau bang thi dung vai
    hon la nam lan giua cac o meta.
    - DAT NGOAI ternary lines.length===0: phieu chua co NCC trung thau van phai
      biet dang lap ke hoach cho goi viec nao (man rong nhin van gon nen khong
      ai keu — dung ca "vang-mat trong giong on").

(2) "+ them hang muc o day": khoi "Them hang muc cho:" doi TU DUOI BANG vao
    thanh dong cuoi tbody, ngay tren dong TONG. colSpan=4 (bang da len 4 cot
    tu dau luot S177). Panel nhap addFor VAN o ngoai bang — form nhieu o, nhet
    vao <table> thi vo luoi table-fixed dang giu 3 cot so thang hang.
    Da doi chieu khong nhan doi nut (3 hit "Them hang muc cho:" = 1 nut moi +
    1 chu thich + 1 header panel, khac viec).

(3) "nut edit": anh chot BO ("a vay thoi khoi"). Man nhap da sua duoc TRUC TIEP
    tren dong (dropdown + o so + o ghi chu, tu luu khi roi o) nen nut nay se la
    duong sua thu hai song song. O trong o cot 4 la do dong den tu phieu Duyet
    NCC (peReferenceAmount != 0) nen nut xoa bi chan CO CHU DICH tu S175 —
    khong phai thieu nut.

GIU ma du an lap tren tung dong (anh: "de sau nay nguoi ta de hinh dung hang
muc to no chua cac hang muc con nao") — dung thiet ke chot dau luot S177.

Diff CHI 2 file FE, 0 file backend => .NET test khong the bi anh huong, bo qua
vong test do (khai ro). 2 app build sach · lint 0 keu file nay · SHA IDENTICAL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-08-06 15:22:00 +07:00
parent 0ce00108d8
commit b346b41561
2 changed files with 98 additions and 58 deletions

View File

@ -452,12 +452,10 @@ export function KhkkDetailContent({
<Field label="Mã kế hoạch" value={plan.maKeHoach ?? '—'} mono />
<Field label="Dự án" value={plan.projectName ?? '—'} />
<Field label="Người soạn" value={plan.drafterFullName ?? '—'} />
{/* [S175 — anh: "hạng mục của Dự án, Duyệt NCC ấy"] Đây là HẠNG MỤC CÔNG VIỆC của
dự án, lấy từ phiếu Duyệt NCC nguồn (`PE.WorkItemId`).
🔴 KHÁC hẳn `line.tenHangMuc` — cái đó là danh-mục SP-002 dùng phân loại HỢP
ĐỒNG (86 dòng, chọn ở mục 2). Hai thứ trùng tên tiếng Việt nhưng khác trục;
lượt trước lead bày nhầm cái SP-002 ở đây nên anh bảo "Bỏ đi". */}
<Field label="Hạng mục (dự án)" value={plan.peWorkItemName ?? '—'} />
{/* 🧊 [S177 — anh: "Làm header dưới đây"] Ô **Hạng mục (dự án)** ĐÃ DỜI xuống làm
header của mục 2 (xem ngay dưới). Nó là gói việc mà CẢ BẢNG hạng mục thuộc về,
nên đứng đầu bảng thì đúng vai hơn là nằm lẫn giữa các ô meta ở đây.
🔸 Chú thích S175 cũ ("xếp NGAY DƯỚI Dự án ở cột PHẢI") hết hiệu lực theo. */}
<Field label="Ngày tạo" value={formatDateTime(plan.createdAt)} />
</div>
@ -489,6 +487,19 @@ export function KhkkDetailContent({
) : undefined
}
>
{/* [S177 — anh: "Làm header dưới đây"] HẠNG MỤC (DỰ ÁN) = gói việc mà CẢ bảng dưới
thuộc về, dời từ mục 1 xuống đây làm header của mục 2.
🔴 ĐẶT NGOÀI ternary `lines.length === 0`: phiếu chưa có NCC trúng thầu thì vẫn
phải biết mình đang lập kế hoạch cho gói việc nào — nhét vào nhánh có-dòng là
đúng ca "vắng-mặt trông giống ổn" (màn rỗng nhìn vẫn gọn nên không ai kêu). */}
<div className="mb-3 flex flex-wrap items-baseline gap-x-2 gap-y-0.5 border-b border-slate-200 pb-2">
<span className="label-eyebrow text-slate-500">Hạng mục (dự án)</span>
{plan.peWorkItemCode && (
<span className="font-mono text-[11px] font-medium text-slate-500">{plan.peWorkItemCode}</span>
)}
<span className="text-sm font-semibold text-slate-800">{plan.peWorkItemName ?? '—'}</span>
</div>
{plan.lines.length === 0 ? (
<div className="text-sm italic text-slate-400">Phiếu nguồn chưa NCC trúng thầu.</div>
) : (
@ -608,6 +619,33 @@ export function KhkkDetailContent({
</td>
</tr>
))}
{/* [S177 — anh: "+ thêm hạng mục ở đây"] Nút thêm DỜI TỪ DƯỚI BẢNG vào
thành dòng cuối của `tbody` — ngay sau dòng dữ liệu cuối, TRÊN dòng TỔNG.
🔴 `colSpan={4}` (không phải 3): bảng đã lên 4 cột từ đầu lượt S177.
🔸 Panel nhập (`addFor`) VẪN Ở NGOÀI bảng — nó là form nhiều ô, nhét vào
trong `<table>` thì vỡ lưới `table-fixed` đang giữ 3 cột số thẳng hàng. */}
{isDraft && (
<tr>
<td colSpan={4} className="px-3 py-2">
<div className="flex flex-wrap items-center gap-1.5">
<span className="text-[11px] text-slate-500">Thêm hạng mục cho:</span>
{[...new Map(plan.lines.map(l => [l.supplierId, l])).values()].map((l) => (
<button
key={l.supplierId}
type="button"
onClick={() => {
setAddFor({ supplierId: l.supplierId, supplierName: l.supplierName })
setAddCatalogId(''); setAddAmount(''); setAddNote('')
}}
className="inline-flex items-center gap-1 rounded border border-brand-300 px-2 py-1 text-[11px] font-medium text-brand-700 transition hover:bg-brand-50"
>
<Plus className="h-3 w-3" /> {l.supplierName ?? '(không rõ NCC)'}
</button>
))}
</div>
</td>
</tr>
)}
</tbody>
{/* [S175 — anh: "chọn hạng mục tiền → thành tổng"] TỔNG = Σ giá các dòng.
🔴 KHÔNG có cột "giá tham chiếu" / "giá duyệt" nữa — anh chốt "ko cần so
@ -628,29 +666,11 @@ export function KhkkDetailContent({
</table>
</div>
{/* [S175 — anh chốt] THÊM HẠNG MỤC cho 1 NCC. 1 NCC được N hạng mục: máy đã cho
phép từ Mig 71 (unique 3 cột `Plan × Supplier × CatalogEntry`), lượt này chỉ
bung UI — đúng khuôn "máy rộng, UI hẹp" anh chốt S164.
🔴 CHỈ liệt kê NCC ĐÃ CÓ trên phiếu: NCC đến từ winner của phiếu Duyệt NCC;
đẻ NCC mới ở đây là ký với nhà thầu chưa ai chọn (BE cũng chặn, 409). */}
{isDraft && (
<div className="mt-3 flex flex-wrap items-center gap-1.5">
<span className="text-[11px] text-slate-500">Thêm hạng mục cho:</span>
{[...new Map(plan.lines.map(l => [l.supplierId, l])).values()].map((l) => (
<button
key={l.supplierId}
type="button"
onClick={() => {
setAddFor({ supplierId: l.supplierId, supplierName: l.supplierName })
setAddCatalogId(''); setAddAmount(''); setAddNote('')
}}
className="inline-flex items-center gap-1 rounded border border-brand-300 px-2 py-1 text-[11px] font-medium text-brand-700 transition hover:bg-brand-50"
>
<Plus className="h-3 w-3" /> {l.supplierName ?? '(không rõ NCC)'}
</button>
))}
</div>
)}
{/* 🧊 [S177 — anh: "+ thêm hạng mục ở đây"] Khối "Thêm hạng mục cho:" ĐÃ DỜI vào
trong `tbody` (dòng cuối, trên TỔNG). Lý lẽ S175 vẫn đúng và đi theo lên trên:
1 NCC được N hạng mục từ Mig 71, và CHỈ liệt kê NCC ĐÃ CÓ trên phiếu — đẻ NCC
mới ở đây là ký với nhà thầu chưa ai chọn (BE cũng chặn, 409).
🔴 Giữ 1 dòng vết vì đây là chỗ người sau sẽ tìm khi hỏi "nút thêm đâu rồi". */}
{addFor && (
<div className="mt-2 rounded-md border border-brand-200 bg-brand-50/50 p-3">